【问题标题】:ValidationRule with parameters带参数的 ValidationRule
【发布时间】:2015-04-23 02:50:17
【问题描述】:

我正在使用 C# 和 WPF 开发一个应用程序,我有自己的滑块自定义控件。和文本框在同一个窗口上。我的滑块的所有属性都是DependencyProperty

我使用文本框来更改滑块的属性。我想在文本框上使用ValidationRule。我编写了自己的 ValidationRule(派生自 ValidationRule 类)。我想将一些参数传递给ValidationRule。这是代码:

文本框:

<TextBox HorizontalAlignment="Left" Height="24" Margin="10,169,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="40" Style="{DynamicResource archiveSearchTextBox}" MaxLength="3" HorizontalContentAlignment="Center" TabIndex="2">
        <TextBox.Text>
            <Binding UpdateSourceTrigger="PropertyChanged" Mode="TwoWay" ElementName="gammaSlider" Path="LeftThumbValue" NotifyOnValidationError="True" ValidatesOnExceptions="True" ValidatesOnDataErrors="True">
                <Binding.ValidationRules>
                    <ExceptionValidationRule/>
                    <local:ZeroTo255MinMax>
                        <local:ZeroTo255MinMax.Parameters>
                            <local:ValidationParameters NumberCombineTo="{Binding ElementName=gammaSlider, Path=RightThumbValue}" ValTypeFor0to255="ShouldBeSmaller"/>
                        </local:ZeroTo255MinMax.Parameters>
                    </local:ZeroTo255MinMax>
                </Binding.ValidationRules>
            </Binding>
        </TextBox.Text>
    </TextBox>

ZeroTo255MinMax 验证规则:

 public class ZeroTo255MinMax : ValidationRule
{
    private ValidationParameters _parameters = new ValidationParameters();
    public ValidationParameters Parameters
    {
        get { return _parameters; }
        set { _parameters = value; }
    }

    public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
    {
        string numberStr = value as string;
        int val;

        if (int.TryParse(numberStr, out val))
        {
            if (val < 0 || val > 255)
                return new ValidationResult(false, "");
            else if (Parameters.ValTypeFor0to255 == ValidationParameters.ValTypes.ShouldBeBigger)
            {
                if (val <= Parameters.NumberCombineTo || val - Parameters.NumberCombineTo < 2)
                    return new ValidationResult(false, "");
            }
            else if (Parameters.ValTypeFor0to255 == ValidationParameters.ValTypes.ShouldBeSmaller)
            {
                if (val >= Parameters.NumberCombineTo || Parameters.NumberCombineTo - val < 2)
                    return new ValidationResult(false, "");
            }
            return new ValidationResult(true, "");
        }
        else
            return new ValidationResult(false, "");
    }
}

public class ValidationParameters : DependencyObject
{
    public enum ValTypes { ShouldBeSmaller, ShouldBeBigger };
    public static readonly DependencyProperty NumberCombineToProperty = DependencyProperty.Register("NumberCombineTo", typeof(int), typeof(ValidationParameters), new PropertyMetadata(0, new PropertyChangedCallback(OnNumberCombineToChanged)));
    public static readonly DependencyProperty ValTypeFor0to255Property = DependencyProperty.Register("ValTypeFor0to255", typeof(ValTypes), typeof(ValidationParameters), new PropertyMetadata(ValTypes.ShouldBeBigger));

    private static void OnNumberCombineToChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { d.CoerceValue(NumberCombineToProperty); }

    public int NumberCombineTo
    {
        get { return (int)GetValue(NumberCombineToProperty); }
        set { SetValue(NumberCombineToProperty, value); }
    }

    public ValTypes ValTypeFor0to255
    {
        get { return (ValTypes)GetValue(ValTypeFor0to255Property); }
        set { SetValue(ValTypeFor0to255Property, value); }
    }
}

我的猜测是,一切都很好,但问题是,NumberCombineTo 参数设置为default (0) 即使我更改了 gammaSlider 的 RightThumbValue 属性。 当RightThumbValue 更改时,我需要更新NumberCombineTo 属性。

【问题讨论】:

  • 很难说没有a good, minimal, complete code example。你看过调试器的“输出”窗口吗?那里有使用诊断信息吗?您是否确认绑定到RightThumbValueValidationRule 场景之外有效?您发布的代码对我来说似乎没问题,但这并没有说明什么;我在 WPF 中尝试了很多看起来应该可以工作的东西,但实际上却没有。 :)
  • 我已经逐行调试了代码。 RightThumbValue 正在发生应有的变化。我认为验证的 binding 部分是错误的。因为NumberCombineTo 没有改变。即使RightThumbValue 已更改,它也具有DependencyProperty's 默认值。
  • 再次:您是否在调试器的“输出”窗口中看到任何诊断消息?绑定失败通常会生成某种有用的消息。查看您的代码,唯一值得怀疑的是您对LeftThumbValueRightThumbValue 绑定使用了不同的ElementName 值。当然,如果您实际上有两个不同的双拇指滑块,这可能没问题;但如果您希望两者都绑定到同一个滑块,则使用两个不同的名称可能是错误的(即使用 gammaSlidermyOwnSlider,但不能同时使用两者)。
  • 我很抱歉其中两个是“gammaSlider”。 gammaSlider 类似于THIS ONE

标签: c# wpf validationrules


【解决方案1】:

我根据您在此处提供的 sn-ps 编写了一个简单的完整代码示例,我相信我能够重现您遇到的基本问题。

如果您在调试器中运行代码,并查看“输出”窗口,您可能会看到一条消息,部分内容如下:

找不到目标元素的管理 FrameworkElement 或 FrameworkContentElement

WPF 绑定系统需要这些元素之一才能查找源元素的名称(即ElementName 属性的对象)。但在这种情况下,您试图绑定一个对象的属性,该对象本身既不是与框架相关的元素,也不是以 WPF 可见的方式与一个元素相关。因此在尝试配置绑定时失败。

我看过几篇不同的文章建议通过“代理对象”解决此问题。这通常遵循声明绑定到包含对象的DataContext 的资源,然后将该对象用作绑定的Source 的模式。但对我来说,正确设置这个设置似乎很棘手,这取决于能否设置一个特定的 DataContext 对象,该对象包含您实际上想要绑定的属性。随着无框架元素绑定数量的增加,您可以采用这种技术的程度有限。

例如:
How to bind to data when the DataContext is not inherited
Attaching a Virtual Branch to the Logical Tree in WPF
甚至在这里,WPF Error: Cannot find govering FrameworkElement for target element

相反,在我看来,这是代码隐藏效果更好的情况之一。只需几行代码即可显式设置绑定,完全避免了WPF是否真的能找到要绑定的对象的问题。

我最终得到了一个如下所示的 XAML 示例:

<Window x:Class="TestSO28645688ValidationRuleParameter.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:TestSO28645688ValidationRuleParameter"
        Title="MainWindow" Height="350" Width="525">
  <Window.Resources>
    <local:ValidationParameters x:Key="validationParams1"
                                    ValTypeFor0to255="ShouldBeSmaller"/>
  </Window.Resources>
  <StackPanel>
    <Slider x:Name="slider1" Value=".25" />
    <Slider x:Name="slider2" Value=".5"/>
    <TextBlock Text="{Binding ElementName=slider1, Path=Value,
               StringFormat=slider1.Value: {0}}" />
    <TextBlock Text="{Binding ElementName=slider2, Path=Value,
               StringFormat=slider2.Value: {0}}" />
    <TextBlock Text="{Binding Source={StaticResource validationParams1},
                              Path=NumberCombineTo,
                              StringFormat=validationParams1.NumberCombineTo: {0}}" />
    <TextBox x:Name="textBox1" HorizontalAlignment="Left" VerticalAlignment="Top"
             Height="24" Width="400"
             Margin="5" TextWrapping="Wrap"
             MaxLength="3" HorizontalContentAlignment="Center" TabIndex="2">
      <TextBox.Text>
        <Binding UpdateSourceTrigger="PropertyChanged" Mode="TwoWay"
                 ElementName="slider1" Path="Value" NotifyOnValidationError="True"
                 ValidatesOnExceptions="True" ValidatesOnDataErrors="True">
          <Binding.ValidationRules>
            <ExceptionValidationRule/>
            <local:ZeroTo255MinMax Parameters="{StaticResource validationParams1}"/>
          </Binding.ValidationRules>
        </Binding>
      </TextBox.Text>
    </TextBox>
  </StackPanel>
</Window>

这里与您的代码不同的主要是我将ValidationParameters 对象放在窗口的资源中。这使我可以轻松地从代码隐藏中引用它以进行绑定。

(当然,其余代码也不同,但没有任何意义。我觉得在基本示例中使用两个单独的Slider 控件更简单,因为它内置在 WPF 中,并提供TextBlock 窗口中的元素,以便更轻松地查看正在发生的事情)。

代码隐藏如下:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        Binding binding = new Binding();

        binding.Source = slider2;
        binding.Path = new PropertyPath(Slider.ValueProperty);

        ValidationParameters validationParams = (ValidationParameters)Resources["validationParams1"];

        BindingOperations.SetBinding(validationParams, ValidationParameters.NumberCombineToProperty, binding);
    }
}

换句话说,它只是创建一个新的Binding 对象,分配源对象和属性,检索我们要绑定该对象+属性的ValidationParameters 对象,然后在@987654335 上设置绑定@对象的NumberCombineTo属性。

当我这样做时,NumberCombineTo 属性会随着slider2.Value 值的变化而正确更新,并且在调用ValidationRule 对象的Validate() 方法时可用。

【讨论】:

    猜你喜欢
    • 2018-12-28
    • 1970-01-01
    • 1970-01-01
    • 2013-08-14
    • 1970-01-01
    • 2011-05-19
    • 1970-01-01
    • 2013-09-26
    相关资源
    最近更新 更多