如何避免转换器与后面的 WPF 代码中的多重绑定发生冲突

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12057126/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 05:03:31  来源:igfitidea点击:

How to avoid Converters clashing with multibinding in WPF code behind

c#wpfdynamic-binding

提问by Saggio

I am creating WPF elements dynamically in code behind, and for each of the rows in the GridI'm building it consists of a CheckBoxand a Dynamic number of TextBoxes. The interaction that is needed is the following:

我在后面的代码中动态创建 WPF 元素,对于Grid我正在构建的每一行,它由一个CheckBox和一个动态数量的TextBoxes. 所需的交互如下:

  • If all TextBoxesin a row have a value of 0, set the CheckBoxIsCheckedproperty to trueand Disable it.
  • If one of the TextBoxesis then changed from 0, enable the CheckBoxand set IsCheckedto false.
  • If the user clicks on the CheckBox, set all associated TextBoxesto 0and Disable the CheckBox
  • 如果TextBoxes一行中的所有元素的值为0,则将该CheckBoxIsChecked属性设置为true并禁用它。
  • 如果其中之一TextBoxes从 更改为0,则启用 CheckBox并设置IsCheckedfalse
  • 如果用户单击CheckBox,则将所有关联设置TextBoxes0并禁用CheckBox

I was able to accomplish the first part of the last one using this code:

我能够使用以下代码完成最后一部分的第一部分:

Binding setScoreToZeroIfIsNormalChecked = new Binding("IsChecked");
setScoreToZeroIfIsNormalChecked.Source = this.NormalCheckBoxControl;
setScoreToZeroIfIsNormalChecked.Converter = m_NormalCheckBoxJointScoresConverter;
tempJointScoreControl.JointScoreContainer.SetBinding(ContainerBase.SingleAnswerProperty, setScoreToZeroIfIsNormalChecked);

and the converter:

和转换器:

public object Convert(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
    if (value is bool && targetType == typeof(Answer))
    {
        if ((bool)value)
        {
            Answer answer = new Answer();
            answer.Value = "0";
            answer.DisplayValue = "0";
            return answer;
        }
        else
            return null;
    }
    else
    {
        return null;
    }
}

However, in attempting to create another converter to accomplish other functionality, I was running into issues of converters stepping on one another since all functionality is based around the CheckBox.IsCheckedproperty.

但是,在尝试创建另一个转换器以完成其他功能时,我遇到了转换器相互干扰的问题,因为所有功能都基于该CheckBox.IsChecked属性。

Is there anyway to accomplish all of the above using one or two multibinding converters? I'd really like to avoid having to create a whole bunch of events and maintaining them in order to do this.

无论如何,是否可以使用一个或两个多绑定转换器来完成上述所有操作?我真的很想避免必须创建一大堆事件并维护它们才能做到这一点。

回答by Erti-Chris Eelmaa

It's relatively easy. Everything should resolve around CheckBox IsChecked property. For a simple reason, it's a two-way property. So either you can modify it, or CheckBox can modify it.

这相对容易。一切都应该围绕 CheckBox IsChecked 属性解决。出于一个简单的原因,它是一个双向属性。所以要么你可以修改它,要么 CheckBox 可以修改它。

So what you do, you use MultiBinding, as such:

所以你做什么,你使用 MultiBinding,如下:

    MultiBinding multiBinding = new MultiBinding();
    multiBinding.Converter = multiBindingConverter;

    multiBinding.Bindings.Add(new Binding("Text") { Source = txtbox1});
    multiBinding.Bindings.Add(new Binding("Text") { Source = txtbox2});

    multiBinding.NotifyOnSourceUpdated = true;//this is important. 
    checkBox.SetBinding(CheckBox.IsCheckedProperty, multiBinding);

And in your multiBindingConverter, you will have object[] value as first parameter, which you need to convert into IList and iterate over it && do your calculations, if you should either return true/false.(IsChecked=true or false)

在您的 multiBindingConverter 中,您将有 object[] 值作为第一个参数,您需要将其转换为 IList 并对其进行迭代 && 进行计算,如果您应该返回 true/false.(IsChecked=true 或 false)

Now bind CheckBox IsEnabled to CheckBox IsChecked property, and use BooleanInverterConverter. (If CheckBox is checked, it should be disabled, and vice versa)

现在将 CheckBox IsEnabled 绑定到 CheckBox IsChecked 属性,并使用 BooleanInverterConverter。(如果 CheckBox 被选中,它应该被禁用,反之亦然)

The last step is to make TextBoxes listen to actual IsChecked property of CheckBox. If it is TRUE, they all should show value of 0, otherwise they can show what they want.

最后一步是让 TextBoxes 监听 CheckBox 的实际 IsChecked 属性。如果它是 TRUE,它们都应该显示 0 值,否则它们可以显示他们想要的。

So, make a new MultiBinding.

因此,创建一个新的 MultiBinding。

    MultiBinding multiBinding = new MultiBinding();
    multiBinding.Converter = textboxMultiBindingConverter;

    multiBinding.Bindings.Add(new Binding("IsChecked") { Source = checkbox1});
    multiBinding.Bindings.Add(new Binding("Text") { Source = textbox1});

    multiBinding.NotifyOnSourceUpdated = true;//this is important. 
    textbox1.SetBinding(TextBox.Text, multiBinding);

the idea in textboxMultiBindingConverter is to either return Text(value[1]) if value[0]==FALSE or "0" if value[0]==TRUE.

textboxMultiBindingConverter 中的想法是,如果 value[0]==FALSE 或“0”,则返回 Text(value[1]) 如果 value[0]==TRUE。

回答by Daniel Hilgarth

This problem can be solved very easily if you would use MVVM.

如果您使用 MVVM,这个问题可以很容易地解决。

You would have a ViewModel that represents a row in the grid. It would have a property per textbox and one for the checkbox.

您将拥有一个代表网格中一行的 ViewModel。每个文本框都有一个属性,复选框有一个属性。

Additionally you would have a ViewModel for the View containing the Grid and this ViewModel would expose a collection of row ViewModels.

此外,您将为包含网格的视图创建一个 ViewModel,并且此 ViewModel 将公开一组行 ViewModel。

The ViewModel for your row:

您行的 ViewModel:

public class AnswersViewModel : ViewModelBase // From MvvmLight
{
    public bool IsAnswered
    {
        get { return _isAnswered; }
        set
        {
            if(value == _isAnswered)
                return;
            _isAnswered = value;
            if(_isAnswered)
            {
                Answer1 = "0";
                Answer2 = "0";
            }

            RaisePropertyChanged("IsAnswered");
        }
    }

    public string Answer1
    {
        get { return _answer1; }
        set
        {
            if(value == _answer1)
                return;

            _answer1 = value;
            RaisePropertyChanged("Answer1");

            if(_answer1 == "0" && _answer2 == "0")
            {
                _isAnswered = true;
                RaisePropertyChanged("IsAnswered");
            }
        }
    }

    // The implementation of Answer2 is similar to Answer1
}

The ViewModel for the View:

视图的视图模型:

public class FooViewModel : ViewModelBase
{
    public ObservableCollection<AnswersViewModel> Answers
    {
        get { return _answers; }
    }
}

Your View would contain the Grid with ItemsSource="{Binding Answers}"and a ControlTemplate for the items which binds to the properties of AnswersViewModel.

您的视图将包含 Grid withItemsSource="{Binding Answers}"和一个 ControlTemplate 用于绑定到AnswersViewModel.

Disabling the CheckBox I would handle via a Trigger in a Style.

禁用 CheckBox 我将通过样式中的触发器处理。