如何通过静态事件将信息从附加行为传递到视图模型

本文关键字:模型 视图 静态 何通过 事件 信息 | 更新日期: 2023-09-27 18:07:04

我有一个附加属性到我的视图中的文本框。附加的属性对文本框输入执行验证并执行其他工作。附加的属性验证例程引发视图模型正在监视的事件。

  1. 这是否"违反"MVVM推理有视图模型获得无效的文本框?
  2. 当包含文本框的用户控件被删除时,GC将如何处理附加属性的静态事件?
  3. 如果需要特定的代码来避免内存泄漏,如何做到这一点?
  4. 是否有更好的方法来做到这一点?

很抱歉有这么长的列表,但是Google并没有解决这个问题。

任何和所有的帮助是感激。谢谢您的考虑。

(VS2010 .net 4.5)

TIA

ViewModel

class CheckInViewModel : SimpleViewModelBase
    {
        public CheckInViewModel()
        {
            InValidTextBoxes = new List<TextBox>();
            Stargate_V.Helpers.ColorMaskingTextBoxBehavior.Validated += (sender, e) =>
                {
                    if (e.valid)
                        InValidTextBoxes.Remove(e.sender);
                    else
                        InValidTextBoxes.Add(e.sender);
                };
        }
        List<TextBox> InValidTextBoxes;

    }

XAML

 <TextBox 
            h:ColorMaskingTextBoxBehavior.Mask="^[MmFf]$"
            Text="{Binding Sex}"
            Height="24" HorizontalAlignment="Right" Margin="0,55,665,0" VerticalAlignment ="Top" Width="36" />

附加分级

  public class ColorMaskingTextBoxBehavior : DependencyObject
    {
        // Entrance point from Xaml 
        public static readonly DependencyProperty MaskProperty = DependencyProperty.RegisterAttached("Mask",
                typeof(string),
                typeof(ColorMaskingTextBoxBehavior),
                new FrameworkPropertyMetadata(OnMaskChanged));
...........................
 // Callback from XAML initialization of the attached property.
    private static void OnMaskChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
    {
        var textBox = dependencyObject as TextBox;
        var mask = e.NewValue as string;
        textBox.PreviewTextInput -= textBox_PreviewTextInput;
        textBox.PreviewKeyDown -= textBox_PreviewKeyDown;
        DataObject.RemovePastingHandler(textBox, Pasting);
        DataObject.RemoveCopyingHandler(textBox, NoDragCopy);
        CommandManager.RemovePreviewExecutedHandler(textBox, NoCutting);

        if (mask == null)
        {
            textBox.ClearValue(MaskProperty);
            textBox.ClearValue(MaskExpressionProperty);
        }
        else
        {
            textBox.SetValue(MaskProperty, mask);
            SetMaskExpression(textBox, new Regex(mask, RegexOptions.Compiled | RegexOptions.IgnorePatternWhitespace));
            textBox.PreviewTextInput += textBox_PreviewTextInput;
            textBox.PreviewKeyDown += textBox_PreviewKeyDown;
            DataObject.AddPastingHandler(textBox, Pasting);
            DataObject.AddCopyingHandler(textBox, NoDragCopy);
            CommandManager.AddPreviewExecutedHandler(textBox, NoCutting);
        }
    }

private static void textBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
        {
            var textBox = sender as TextBox;
            var maskExpression = GetMaskExpression(textBox);
            string passHex = (string)textBox.GetValue(PassColorProperty);
            string failHex = (string)textBox.GetValue(FailColorProperty);
            Color passColor = Extensions.ToColorFromHex(passHex);
            Color failColor = Extensions.ToColorFromHex(failHex);
            if (maskExpression == null)
            {
                return;
            }
            var proposedText = GetProposedText(textBox, e.Text);
            if (!maskExpression.IsMatch(proposedText))
            {
                textBox.Background = new SolidColorBrush(failColor);
                ValidationEventArgs args = new ValidationEventArgs();
                args.sender = textBox;
                args.valid = false;
                OnValidation(args);
            }
            else
            {
                textBox.Background = new SolidColorBrush(passColor);
                ValidationEventArgs args = new ValidationEventArgs();
                args.sender = textBox;
                args.valid = true;
                OnValidation(args);
            }
        }

从上面代码调用的事件

    public static event EventHandler<ValidationEventArgs> Validated;
    static void OnValidation(ValidationEventArgs e)
    {
        EventHandler<ValidationEventArgs> handler = Validated;
        if (handler != null)
        {
            handler(null, e);
        }
    }

public class ValidationEventArgs : EventArgs
{
    public TextBox sender;
    public bool valid;
}

如何通过静态事件将信息从附加行为传递到视图模型

是的,我认为这违反了MVVM。视图模型不应该知道视图的任何信息。总是要问自己的问题是"我能在不创建任何视图的情况下运行应用程序吗?"在这种情况下,你的视图模型直接与一个文本框列表交互,所以模式被打破了。

这里有几种方法可以实现你的目标,可能最简单的是在视图模型中创建一个处理程序,当你的TextBox文本发生变化时调用:
public delegate void ValidationDelegate(bool isValid);
public class MyViewModel : ViewModelBase
{
    public ValidationDelegate ValidationHandler { get { return (isValid) => OnValidate(isValid); } }
    private void OnValidate(bool isValid)
    {
        // handle the validation event here
    }
}

现在你所需要的是一个带有附加属性的行为,你可以绑定到这个处理程序:

public class ValidateBehavior : Behavior<TextBox>
{
    public ValidationDelegate Validated
    {
        get { return (ValidationDelegate)GetValue(ValidatedProperty); }
        set { SetValue(ValidatedProperty, value); }
    }
    public static readonly DependencyProperty ValidatedProperty =
        DependencyProperty.Register("Validated", typeof(ValidationDelegate), typeof(ValidateBehavior), new PropertyMetadata(null));
    protected override void OnAttached()
    {
        base.OnAttached();
        this.AssociatedObject.TextChanged += ValidateText;
    }
    protected override void OnDetaching()
    {
        base.OnDetaching();
        this.AssociatedObject.TextChanged -= ValidateText;
    }
    private void ValidateText(object sender, TextChangedEventArgs e)
    {
        if (this.Validated != null)
        {
            bool isValid = true; // do text validation here
            this.Validated(isValid);
        }
    }
}

然后最后将行为添加到有问题的TextBox并绑定处理程序:

    <TextBox>
        <i:Interaction.Behaviors>
            <behaviors:ValidateBehavior Validated="{Binding ValidationHandler}"/>
        </i:Interaction.Behaviors>
    </TextBox>
编辑:如果你不想使用混合行为,那么你也可以使用附加行为:
public static class ValidateBehavior
{
    public static ValidationDelegate GetValidate(TextBox textbox)
    {
        return (ValidationDelegate)textbox.GetValue(ValidateProperty);
    }
    public static void SetValidate(TextBox textbox, ValidationDelegate value)
    {
        textbox.SetValue(ValidateProperty, value);
    }
    public static readonly DependencyProperty ValidateProperty =
        DependencyProperty.RegisterAttached(
        "Validate",
        typeof(ValidationDelegate),
        typeof(ValidateBehavior),
        new UIPropertyMetadata(null, OnValidateChanged));
    static void OnValidateChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
    {
        var textbox = depObj as TextBox;
        if (textbox == null)
            return;
        if (e.OldValue is ValidationDelegate)
            textbox.TextChanged -= OnTextChanged;
        if (e.NewValue is ValidationDelegate)
            textbox.TextChanged += OnTextChanged;
    }
    static void OnTextChanged(object sender, RoutedEventArgs e)
    {
        if (!Object.ReferenceEquals(sender, e.OriginalSource))
            return;
        var textbox = e.OriginalSource as TextBox;
        if (textbox != null)
        {
            var validate = GetValidate(textbox);
            if (validate != null)
            {
                bool isValid = true; // do text validation here
                validate(isValid);
            }
        }
    }
}
和相应的XAML:
<TextBox behaviors:ValidateBehavior.Validate="{Binding ValidationHandler}" />