简体   繁体   English

将对象绑定到组合框将填充列表,但是CSharp中的对象中的选择更改不会更新

[英]Binding object to combobox populates list, but selection changes are not updated in the object in CSharp

I have a class ParamObj (which implements INotifyPropertyChanged ) which contains a property DefaultValues of type ObservableCollection<Param> . 我有一个类ParamObj (实现INotifyPropertyChanged ),其中包含类型为ObservableCollection<Param>的属性DefaultValues My Param class (which also implements INotifyPropertyChanged ) contains two properties: Name and IsSelected . 我的Param类(也实现INotifyPropertyChanged )包含两个属性: NameIsSelected

On button click I am populating a StackPanel with TextBlock and ComboBox controls. 在按钮上单击,我正在用TextBlockComboBox控件填充StackPanel Each Param in the collection DefaultValues is an option in the ComboBox , where the Name property represents the text of that option and IsSelected is a boolean value representing if that option is the one currently selected in the ComboBox . 集合DefaultValues中的每个ParamComboBox一个选项,其中Name属性表示该选项的文本, IsSelected是一个布尔值,表示该选项是否是ComboBox当前选择的选项。

Param class below: Param类如下:

public class Param : INotifyPropertyChanged
{
    public enum ParamType
    {
        Unknown=-1,
        Text=0,
        Option=1,
        Multi=2
    }

    private string pName = string.Empty;
    private bool pSelected = false;
    private ParamType pType = ParamType.Unknown;

    public string Name { get { return pName; } set { pName = value; OnPropertyChanged("Name"); } }
    public bool IsSelected { get { return pSelected; } set { pSelected = value; OnPropertyChanged("IsSelected"); } }
    public ParamType Type { get { return pType; } }

    public Param(string name, bool selected)
    {
        this.pName = name;
        this.pSelected = selected;
        this.pType = ParamType.Option;
    }

    //INotifyPropertyChanged base
    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string name)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
            handler(this, new PropertyChangedEventArgs(name));
    }
}

ParamObj class below: 下面的ParamObj类:

public class ParamObj : INotifyPropertyChanged
{
    //ui type mapping
    public enum UITypeEnum
    {
        Textbox = 0,
        ComboBox = 1,
        CheckableComboBox = 2
    }

    private string name = string.Empty;
    private UITypeEnum uiType = UITypeEnum.Textbox;
    private ObservableCollection<Param> defaultVals = new ObservableCollection<Param>();
    private string propDescription = string.Empty;

    public string Name { get { return name; } set { name = value; OnPropertyChanged("Name"); } }
    public UITypeEnum UIType { get { return uiType; } set { uiType = value; OnPropertyChanged("UIType"); } }
    public ObservableCollection<Param> DefaultValues { get { return defaultVals; } }
    public string Desc { get { return propDescription; } set { propDescription = value; } }

    //Initialization
    public ParamObj()
    {

    }

    public void ClearDefaultValues()
    {
        this.defaultVals.Clear();
        OnPropertyChanged("DefaultValues");
    }

    public void AddDefaultValue(Param objAdded)
    {
        this.defaultVals.Add(objAdded);
    }

    public void AddDefaultValueRange(Param[] objsToAdd)
    {
        foreach (Param o in objsToAdd)
        {
            this.defaultVals.Add(o);
        }
    }

    //INotifyPropertyChanged base
    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string name)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
            handler(this, new PropertyChangedEventArgs(name));
    }
}

Code below is called in the button click event. 下面的代码在按钮单击事件中调用。 po represents a ParamObj instance. po表示ParamObj实例。

case ParamObj.UITypeEnum.ComboBox:
    cb = new ComboBox();
    cb.Height = 26;
    cb.Width = 210;
    cb.Margin = new Thickness(2);
    cb.VerticalContentAlignment = System.Windows.VerticalAlignment.Center;
    cb.DataContext = po.DefaultValues;
    //binding
    cb.ItemsSource = po.DefaultValues;
    cb.DisplayMemberPath = "Name";                        
    cb.SelectedValuePath = "IsSelected";
    cb.SelectedValue = true;

    cb.SelectionChanged += ParamComboBox_SelectionChanged;
    break;

Here's the problem. 这是问题所在。 ComboBox items are populated successfully and the option ( Param object) that has an IsSelected value of true is selected. ComboBox项目已成功填充,并且选择了IsSelected值为true的选项( Param对象)。 What I thought would happen is that when I changed the selected option in the ComboBox the IsSelected property's value (boolean) of the Param object would be toggled from false to true . 我以为会发生什么事,当我在ComboBox更改selected选项时, Param对象的IsSelected属性值(布尔值)将从false切换为true This didn't happen. 这没有发生。 So I added the cb.SelectionChanged += ParamComboBox_SelectionChanged event to the ComboBox . 所以我将cb.SelectionChanged += ParamComboBox_SelectionChanged事件添加到了ComboBox

    private void ParamComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        if (!(sender is ComboBox)) return;

        ComboBox cb = sender as ComboBox;
        Param cp = (Param)cb.SelectedItem;
        if (cp.IsSelected == false)
        {
            foreach (Param p in cb.ItemsSource)
            {
                p.IsSelected = false;
            }
            cp.IsSelected = true;
        }
    }

Through debugging I do see that the IsSelected properties are correctly updated, but the values in my po object are not updated with the new values. 通过调试,我确实看到IsSelected属性已正确更新,但是po对象中的值未使用新值更新。

Any suggestions are greatly appreciated. 任何建议,不胜感激。 I've tried my darndest to figure this out but I'm stuck. 我已经尽我最大的努力解决了这个问题,但是我被困住了。

Brian 布赖恩

This is one of the problems that appear to be very simple, but seems like there is no easy or direct way to bind the ComboBoxItem IsSelected to a given property. 这是看起来很简单的问题之一,但是似乎没有简单或直接的方法将ComboBoxItem IsSelected绑定到给定的属性。 One possible solution that comes to my mind could be: 我想到的一种可能的解决方案可能是:

First, Add a Style of type ComboBoxItem to your ComboBox and add a Trigger to that style. 首先,将类型为ComboBoxItem的样式添加到ComboBox中,然后将触发器添加到该样式。 This trigger will fire when you have selected a ComboBoxItem and will call the helper class in step 2. 选择ComboBoxItem时将触发此触发器,并在步骤2中调用帮助程序类。

     <ComboBox Name="TestCombo" ItemsSource="{Binding Path=DataSource.DefaultValues}" DisplayMemberPath="Name" SelectedValuePath="IsSelected" SelectionChanged="Test_SelectionChanged">
                <ComboBox.Resources>                    
                    <Style TargetType="{x:Type ComboBoxItem}">
                        <Style.Triggers>
                            <Trigger Property="IsSelected" Value="True">
                                <Setter Property="StackOverflow:IsSelectedBehavior.IsSelected" Value="True"></Setter>
                            </Trigger>
                            <Trigger Property="IsSelected" Value="False">
                                <Setter Property="StackOverflow:IsSelectedBehavior.IsSelected" Value="False"></Setter>
                            </Trigger>
                        </Style.Triggers>
                    </Style>
                </ComboBox.Resources>
            </ComboBox>

If you want to do this same thing programmatically, then you go: 如果要以编程方式执行此操作,则可以执行以下操作:

        Style style = new Style();
        Trigger t = new Trigger();
        style.TargetType = typeof(ComboBoxItem);
        t.Property = ListBoxItem.IsSelectedProperty;
        t.Value = true;
        Setter setter = new Setter(IsSelectedBehavior.IsSelectedProperty, true);
        t.Setters.Add(setter);
        style.Triggers.Add(t);
        cb.Resources.Add(typeof(ComboBoxItem), style);

Then, Create a IsSelectedBehaviorClass to handle your IsSelected Behavior. 然后,创建一个IsSelectedBehaviorClass来处理IsSelected行为。 This static class will receive the currently selected ComboBoxItem and set the Param.IsSelected property accordingly. 该静态类将接收当前选择的ComboBoxItem并相应地设置Param.IsSelected属性。 Something like this: 像这样:

    public static class IsSelectedBehavior
    {
        public static readonly DependencyProperty IsSelectedProperty = DependencyProperty.RegisterAttached(
            "IsSelected", typeof (bool), typeof (IsSelectedBehavior), new UIPropertyMetadata(false, OnIsSelected));

        public static bool GetIsSelected(DependencyObject d)
        {
            return (bool) d.GetValue(IsSelectedProperty);
        }

        public static void SetIsSelected(DependencyObject d, bool value)
        {
            d.SetValue(IsSelectedProperty, value);
        }

        public static void OnIsSelected(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            //Get current ComboBoxItem
            ComboBoxItem test = d as ComboBoxItem;
            //Get the current Param from the ComboBoxItem.Content
            Param p = test.Content as Param;
            //Set the Param.IsSelected property
            p.IsSelected = (bool) e.NewValue;
        }
    }

Finally you can test your solution on the SelectionChanged EventHandler 最后,您可以在SelectionChanged EventHandler上测试您的解决方案

    private void ParamComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            Param selected = cb.SelectedItem as Param;
            if (selected != null)
            {
                Debug.WriteLine("Selection Changed: Selected " + selected.Name);
            }
            StringBuilder str = new StringBuilder();
            foreach (var obj in cb.Items)
            {
                Param p = obj as Param;
                str.Append("Name:" + p.Name + " IsSelected:" + p.IsSelected + Environment.NewLine);
                Debug.WriteLine(str);
            }
            MessageBox.Show(str.ToString());
        }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM