gpt4 book ai didi

c# - 只读值的优雅解决方案

转载 作者:太空狗 更新时间:2023-10-29 22:52:19 26 4
gpt4 key购买 nike

我正在开发一个 WPF 应用程序,其窗口大小和组件位置必须在初始化时动态计算,因为它们基于我使用的主要 UserControl 大小和其他一些次要大小设置。因此,目前,我将这些常量值放在我的 Window 代码中,如下所示:

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

嗯……没什么好说的。它有效......但是它太难看了,我想知道是否有更好的解决方案。我不知道……也许是设置文件、绑定(bind)、内联 XAML 计算或其他任何东西……能让它看起来更好的东西。

最佳答案

我通常将我的所有静态应用程序设置放在一个称为通用类的静态或单例类中,例如 ApplicationSettings(或 MainWindowSettings,如果这些值仅由 主窗口)

如果这些值是用户可配置的,它们将进入 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我写道,让我可以编写一个带有数学表达式的绑定(bind),并将要使用的值传递给它。

我在我的博客上发布的版本只是一个IValueConverter,但很容易扩展成一个IMultiValueConverter,因此它可以接受多个绑定(bind)值。

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

但老实说,如果这些值仅以单一形式使用,那么我只需在 View 后面的代码中的 Loaded 事件中设置值 :)

关于c# - 只读值的优雅解决方案,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15661493/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com