c# - 如何使用静态事件将信息从附加行为传递到 View 模型?

标签 c# wpf mvvm

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

  1. 让 View 模型获取无效的文本框是否“违反”了 MVVM 推理?
  2. 当包含文本框的用户控件被删除时,GC 将如何处理附加属性中的静态事件?
  3. 如果需要特定代码来避免内存泄漏,该怎么做?
  4. 是否有首选方法可以实现此目的?

抱歉,列表太长,但 Google 并未解决这种情况。

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

(VS2010.net 4.5)

TIA

View 模型

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

附属属性(property)

  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。您的 View 模型不应该了解任何 View 。始终要问自己的问题是“我可以在不创建任何 View 的情况下运行我的应用程序吗?”。在这种情况下,您的 View 模型直接与文本框列表交互,因此该模式被破坏。

这里有多种方法可以实现您的目标,最简单的可能是在 View 模型中创建一个处理程序,当您的 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
    }
}

现在您所需要的只是一个带有附加属性的行为,您可以将其绑定(bind)到此处理程序:

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

最后将行为添加到相关文本框并绑定(bind)处理程序:

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

关于c# - 如何使用静态事件将信息从附加行为传递到 View 模型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31897394/

相关文章:

c# - 我可以同时使用 PropertyChanged 和 LostFocus 吗?

c# - 调用异步函数的结果是否会导致无限期阻塞?

wpf - 递归数据模板可能吗?

c# - MVVM 单个对象上的鼠标悬停事件

wpf - 在 TextBlock 中使用绑定(bind)硬编码文本

mvvm - Knockout.js:一个页面或页面的一部分上的多个 ViewModel 绑定(bind)

c# - 停止 TabControl 重新创建其子项

c# - 使用 "using block"时感到困惑 - C#

c# - 在 C# 中动态创建和公开 SOAP 服务及其 WSDL(使用自定义 TCP 监听器!)

c# - 如何关闭网络浏览器控件中的查找窗口