2013-03-27 145 views
4

我正在開發一個WPF應用程序,其窗口大小和組件位置必須在初始化時動態計算,因爲它們基於我使用的主UserControl大小和一些其他次要大小設置。因此,就目前而言,我已經放在那些常量值在我的窗口代碼如下:只讀值的優雅解決方案

public const Double MarginInner = 6D; 
public const Double MarginOuter = 10D; 
public const Double StrokeThickness = 3D; 

public static readonly Double TableHeight = (StrokeThickness * 2D) + (MarginInner * 3D) + (MyUC.RealHeight * 2.5D); 
public static readonly Double TableLeft = (MarginOuter * 3D) + MyUC.RealHeight + MarginInner; 
public static readonly Double TableTop = MarginOuter + MyUC.RealHeight + MarginInner; 
public static readonly Double TableWidth = (StrokeThickness * 2D) + (MyUC.RealWidth * 6D) + (MarginInner * 7D); 
public static readonly Double LayoutHeight = (TableTop * 2D) + TableHeight; 
public static readonly Double LayoutWidth = TableLeft + TableWidth + MarginOuter; 

然後,我只是用他們我的XAML裏面如下:

<Window x:Class="MyNS.MainWindow" ResizeMode="NoResize" SizeToContent="WidthAndHeight"> 
    <Canvas x:Name="m_Layout" Height="{x:Static ns:MainWindow.LayoutHeight}" Width="{x:Static ns:MainWindow.LayoutWidth}"> 

嗯...無話可說。它的工作原理...但它是如此醜陋看到,我想知道是否有任何更好的解決方案。我不知道......也許是一個設置文件,綁定,內聯XAML計算或其他任何東西......這會使它看起來更好。

回答

4

我通常把所有的靜態我的應用程序設置中稱爲東西通用的單一靜態或單例類,像ApplicationSettings

(或MainWindowSettings如果值僅由MainWindow使用)如果值是指用戶可配置的,他們進入app.config並加載到靜態類的構造函數中。如果沒有,我只是在我的靜態類中對它們進行硬編碼,以便以後很容易找到/更改。

public static class ApplicationSettings 
{ 
    public static Double MarginInner { get; private set; } 
    public static Double MarginOuter { get; private set; } 
    public static Double StrokeThickness { get; private set; } 

    static ApplicationSettings() 
    { 
     MarginInner = 6D; 
     MarginOuter = 10D; 
     StrokeThickness = 3D; 
    } 
} 

對於您的XAML計算值,我通常使用一個MathConverter我寫的,讓我寫一個數學表達式的綁定,並通過它的值來使用。

我發佈在我的博客上的版本只是IValueConverter,但它很容易展開爲IMultiValueConverter,因此它可以接受多個綁定值。

<Setter Property="Height"> 
    <Setter.Value> 
     <MultiBinding Converter="{StaticResource MathMultiConverter}" 
        ConverterParameter="(@VALUE1 * 2D) + (@VALUE2 * 3D) + (@VALUE3 * 2.5D)"> 
     <Binding RelativeSource="{x:Static ns:ApplicationSettings.StrokeThickness }" /> 
     <Binding RelativeSource="{x:Static ns:ApplicationSettings.MarginInner}" /> 
     <Binding ElementName="MyUc" Path="ActualHeight" /> 
     </MultiBinding> 
    </Setter.Value> 
</Setter> 

通常我會躲在一個風格這一切混亂的XAML的地方,因此它不會弄亂我的主要XAML代碼,只是應用在需要的地方風格。

這是我使用的轉換代碼的副本IMultiValueConvter

// Does a math equation on a series of bound values. 
// Use @VALUEN in your mathEquation as a substitute for bound values, where N is the 0-based index of the bound value 
// Operator order is parenthesis first, then Left-To-Right (no operator precedence) 
public class MathMultiConverter : IMultiValueConverter 
{ 
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 
    { 
     // Remove spaces 
     var mathEquation = parameter as string; 
     mathEquation = mathEquation.Replace(" ", ""); 

     // Loop through values to substitute placeholders for values 
     // Using a backwards loop to avoid replacing something like @VALUE10 with @VALUE1 
     for (var i = (values.Length - 1); i >= 0; i--) 
      mathEquation = mathEquation.Replace(string.Format("@VALUE{0}", i), values[i].ToString()); 

     // Return result of equation 
     return MathConverterHelpers.RunEquation(ref mathEquation); 
    } 

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) 
    { 
     throw new NotImplementedException(); 
    } 
} 

public static class MathConverterHelpers 
{ 
    private static readonly char[] _allOperators = new[] { '+', '-', '*', '/', '%', '(', ')' }; 

    private static readonly List<string> _grouping = new List<string> { "(", ")" }; 
    private static readonly List<string> _operators = new List<string> { "+", "-", "*", "/", "%" }; 


    public static double RunEquation(ref string mathEquation) 
    { 
     // Validate values and get list of numbers in equation 
     var numbers = new List<double>(); 
     double tmp; 

     foreach (string s in mathEquation.Split(_allOperators)) 
     { 
      if (s != string.Empty) 
      { 
       if (double.TryParse(s, out tmp)) 
       { 
        numbers.Add(tmp); 
       } 
       else 
       { 
        // Handle Error - Some non-numeric, operator, or grouping character found in string 
        throw new InvalidCastException(); 
       } 
      } 
     } 

     // Begin parsing method 
     EvaluateMathString(ref mathEquation, ref numbers, 0); 

     // After parsing the numbers list should only have one value - the total 
     return numbers[0]; 
    } 

    // Evaluates a mathematical string and keeps track of the results in a List<double> of numbers 
    private static void EvaluateMathString(ref string mathEquation, ref List<double> numbers, int index) 
    { 
     // Loop through each mathemtaical token in the equation 
     string token = GetNextToken(mathEquation); 

     while (token != string.Empty) 
     { 
      // Remove token from mathEquation 
      mathEquation = mathEquation.Remove(0, token.Length); 

      // If token is a grouping character, it affects program flow 
      if (_grouping.Contains(token)) 
      { 
       switch (token) 
       { 
        case "(": 
         EvaluateMathString(ref mathEquation, ref numbers, index); 
         break; 

        case ")": 
         return; 
       } 
      } 

      // If token is an operator, do requested operation 
      if (_operators.Contains(token)) 
      { 
       // If next token after operator is a parenthesis, call method recursively 
       string nextToken = GetNextToken(mathEquation); 
       if (nextToken == "(") 
       { 
        EvaluateMathString(ref mathEquation, ref numbers, index + 1); 
       } 

       // Verify that enough numbers exist in the List<double> to complete the operation 
       // and that the next token is either the number expected, or it was a (meaning 
       // that this was called recursively and that the number changed 
       if (numbers.Count > (index + 1) && 
        (double.Parse(nextToken) == numbers[index + 1] || nextToken == "(")) 
       { 
        switch (token) 
        { 
         case "+": 
          numbers[index] = numbers[index] + numbers[index + 1]; 
          break; 
         case "-": 
          numbers[index] = numbers[index] - numbers[index + 1]; 
          break; 
         case "*": 
          numbers[index] = numbers[index] * numbers[index + 1]; 
          break; 
         case "/": 
          numbers[index] = numbers[index]/numbers[index + 1]; 
          break; 
         case "%": 
          numbers[index] = numbers[index] % numbers[index + 1]; 
          break; 
        } 
        numbers.RemoveAt(index + 1); 
       } 
       else 
       { 
        // Handle Error - Next token is not the expected number 
        throw new FormatException("Next token is not the expected number"); 
       } 
      } 

      token = GetNextToken(mathEquation); 
     } 
    } 

    // Gets the next mathematical token in the equation 
    private static string GetNextToken(string mathEquation) 
    { 
     // If we're at the end of the equation, return string.empty 
     if (mathEquation == string.Empty) 
     { 
      return string.Empty; 
     } 

     // Get next operator or numeric value in equation and return it 
     string tmp = ""; 
     foreach (char c in mathEquation) 
     { 
      if (_allOperators.Contains(c)) 
      { 
       return (tmp == "" ? c.ToString() : tmp); 
      } 
      else 
      { 
       tmp += c; 
      } 
     } 

     return tmp; 
    } 
} 

但坦白地說,如果這些值都在一個單一的形式,那麼我剛纔設置的值在Loaded事件僅用於代碼背後的觀點:)

+0

請注意,它應該是'(nextToken ==「(( 「|| double.Parse(nextToken)== numbers [index + 1])''而不是'(double.Parse(nextToken)== numbers [index + 1] || nextToken ==」(「)'to避免解析時崩潰。 – herohuyongtao 2014-05-15 14:56:14

+0

@herohuyongtao噢,應該是。我實際上檢查了if(nextToken ==「(」)'之前的幾行,以便檢查可能會被刪除,我假設'ConverterParameter'是一個有效的數學公式,如果公式無效,應該會崩潰,以便開發人員知道並修復它:) – Rachel 2014-05-15 15:08:38

2

把那些靜態的放在app.config中,它們在那裏會更乾淨。

使用app.config,您首先必須參考System.Configuration

然後你就可以做(有可能是某種類型的鑄造涉及):

ConfigurationManager.AppSettings["MarginInner"]; 

要檢索:

<configuration> 
    <appsettings> 
     <add key="MarginInner" value="6D" /> 
    </appsettings> 
</configuration> 

那麼也許有一個靜態類來保存動態計算,東西如:

public class CalculationHelper 
{ 
    //your dynamic properties in here 
} 
1

mattytommo的回答只處理您擁有的常量值(頁邊距和筆劃厚度),但不包含計算的字段。

我會在什麼馬蒂說一起選擇,添加從app.config文件中檢索恆值的設置類,也做相應的計算需要,然後我可以在XAML

引用相應的屬性

{ Settings.MainWindow.LayoutWidth } 

編輯:

它看起來像馬蒂有相同的,雖然他我之間張貼在編輯;)

+0

大聲笑是啊剛剛說,我已經考慮到,我只是在發佈後自己想過:) – mattytommo 2013-03-27 14:44:57