2013-03-27 4 views
4

내가 사용하는 기본 UserControl 크기 및 기타 작은 크기 설정을 기반으로하기 때문에 초기화시 창 크기와 구성 요소 위치를 동적으로 계산해야하는 WPF 응용 프로그램을 개발 중입니다. 그런 다음읽기 전용 값을위한 우아한 솔루션

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}"> 

을 음 ... 말할 것이 아무것도 없어. 그것은 작동합니다 ...하지만보고 soooo 망할 못생긴이고 나는 이것에 대한 더 나은 해결책이 있는지 궁금 해서요. 나도 몰라 ... 설정 파일, 바인딩, 인라인 XAML 계산 또는 다른 무엇이든지 ... 더 잘 보이게 할 수있는 무언가.

답변

4
값은 사용자가 구성 될 운명하는 경우

(값 만 MainWindow 사용하는 경우 또는 MainWindowSettings)

나는 보통 ApplicationSettings처럼 일반적인 뭔가라는 단일 정적 또는 싱글 톤 클래스의 모든 정적 내 응용 프로그램 설정을 넣어 그들은 app.config에 가서 정적 클래스의 생성자에로드됩니다. 그렇지 않다면 정적 클래스에서 코드를 하드 코딩하기 때문에 나중에 쉽게 찾을 수 있습니다. 당신의 XAML에서 계산 된 값에 대한

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; 
    } 
} 

, 나는 일반적으로 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] || nextToken =="(")'대신에'|| double.Parse (nextToken) == numbers [index + 1] 파싱 ​​할 때 충돌을 피하십시오. – herohuyongtao

+0

@herohuyongtao 오, 그래야합니다. 실제로 if (nextToken == "(")'몇 줄 전에 그 체크를 대신해서 제거 할 수 있는지 확인합니다. 나는'ConverterParameter'가 유효한 수학 공식 일 것이라고 가정하고 이것을 썼습니다. 수식이 유효하지 않아 개발자가 그것에 대해 알고 수정할 수 있으면 충돌이 발생해야합니다. :) – Rachel

2

정적 인 것들을 app.config에 넣으면 훨씬 깨끗합니다.

app.config를 사용하면 먼저 System.Configuration에 대한 참조가 있어야합니다. 그런 다음

당신이 할 수있는 (어떤 종류의 캐스팅이 참여가있을 수 있습니다) :

ConfigurationManager.AppSettings["MarginInner"]; 

는 검색하려면 :

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

그리고 어쩌면, 뭔가를 동적 계산을 유지하는 정적 클래스가 like :

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

mattytommo`s answer 계산 된 필드가 아닌 상수 값 (여백 및 획 두께) 만 처리합니다.

나는, 매티는 말과 연동 해에서 app.config 파일에서 상수 값을 검색하는 설정 클래스를 추가하고 또한 적절한 계산이 필요하지 후 나는 XAML

에서 해당 속성을 참조 할 수있는 것

{ Settings.MainWindow.LayoutWidth } 

편집 :

그가 나를 게시 사이에서 편집으로 매티는하지만 같은 있었다 것 같습니다)

+0

LOL 그래, 내가 이미 그걸 고려해 봤다. 나는 게시 한 직후에 그것에 대해 생각했다. :) – mattytommo

관련 문제