如何使用静态事件将信息从附加行为传递到视图模型? [英] How to pass information from an attached behavior to the viewmodel with static events?

查看:51
本文介绍了如何使用静态事件将信息从附加行为传递到视图模型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have an attached property to textboxes in my view. The attached property performs validation on the textbox input and performs other chores. The attached property validation routine raises an event which is being watched by the viewmodel.

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

抱歉,列表太长,但 Google 没有解决这种情况.

Sorry for the long list, but Google does not address this situation.

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

Any and all help is appreciated. Thank you for your consideration.

(VS2010 .net 4.5)

(VS2010 .net 4.5)

TIA

视图模型

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

Yes, I would argue that this violates MVVM. Your view model should have no knowledge of the views whatsoever. The question to always ask yourself is "can I run my application without creating any views?". In this case your view model is interacting directly with a list of TextBoxes, so the pattern is broken.

这里有多种方法可以实现您的目标,可能最简单的是在您的视图模型中创建一个处理程序,当您的 TextBox 文本更改时调用该处理程序:

There are several ways of achieving your goal here, probably the most simple is to create a handler in your view model that gets called when your TextBox text changes:

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

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

Now all you need is a behavior with an attached property that you can bind to this handler:

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 并绑定处理程序:

And then finally add the behaviour to the TextBox in question and bind the handler:

    <TextBox>
        <i:Interaction.Behaviors>
            <behaviors:ValidateBehavior Validated="{Binding ValidationHandler}"/>
        </i:Interaction.Behaviors>
    </TextBox>

如果您不想使用混合行为,那么您也可以使用附加行为来实现:

If you don't want to use a Blend behaviour then you can also do it with an attached behaviour:

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:

And the corresponding XAML:

<TextBox behaviors:ValidateBehavior.Validate="{Binding ValidationHandler}" />

这篇关于如何使用静态事件将信息从附加行为传递到视图模型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆