ComboBox不会更新SelectedItem

时间:2017-09-08 09:22:49

标签: c# wpf combobox

我正在动态创建一个组合框控件,该控件在运行时添加到视图中。

var comboBox = new ComboBox();
comboBox.Height = 21;
comboBox.Width = 75;
var margin = comboBox.Margin;
margin.Right += 5;
comboBox.Margin = margin;
comboBox.SetBinding(ItemsControl.ItemsSourceProperty, new Binding($"SearchDescriptor.SelectedFilter.FilterControls[{InputPosition}].Conditions"));
comboBox.IsSynchronizedWithCurrentItem = true;
comboBox.SelectedValuePath = "ConditionOperator";
comboBox.DisplayMemberPath = "Text";
comboBox.SetBinding(Selector.SelectedItemProperty, new Binding($"SearchDescriptor.SelectedFilter.FilterControls[{InputPosition}].SelectedCondition"));
Grid.SetColumn(comboBox, 1);
return comboBox;

这是生成组合框控件的代码。控件本身放置在一个视图上,该视图将DataContext设置为某个具有名为

的属性的视图模型
SearchDescriptor

此属性具有选定的过滤器属性,该属性包含控件对象的集合。这些控制对象负责生成控件并设置绑定。

首先我要说的是,最初绑定有效,但是一旦我开始从组合框中选择选项,它就不会更新它绑定的属性。 这很奇怪,因为路径显然是正确的,否则它最初不会绑定属性。

我查看了与此问题有关的所有问题,并尝试了所有方法但无济于事。

SelectedItem绑定到名为SelectedCondition的属性,该属性为Condition类,该类如下所示:

public struct Condition
{
    public ConditionOperator ConditionOperator { get; }
    public string Text { get; }
    public Condition(ConditionOperator conditionOperator, string text)
    {
        ConditionOperator = conditionOperator;
        Text = text;
    }
}

修改 实现属性SelectedCondition

的类
public class SearchDescriptorFilterControl
{
    private SearchDescriptorFilter m_filter;
    private IEnumerable<ConditionOperator> m_choosableConditions;
    private object m_value;

    public SearchDescriptorFilter Filter
    {
        get
        {
            return m_filter;
        }
        set
        {
            if (m_filter != null)
                throw new Exception("Filter can be set only once.");
            m_filter = value;
        }
    }

    public string Label { get; set; }

    public double? LabelWidth { get; set; }

    public HorizontalAlignment LabelContentAlignment { get; set; } = HorizontalAlignment.Left;

    public double Width { get; set; } = 100;

    public string ConditionExpression { get; set; }

    public FilterType Type { get; set; }

    private Condition m_conditionOperator;
    public Condition SelectedCondition
    {
        get
        {
            return m_conditionOperator;
        }
        set
        {
            m_conditionOperator = value;
        }
    }

    public IEnumerable<ConditionOperator> ChoosableConditions
    {
        get
        {
            return m_choosableConditions;
        }
        set
        {
            var mappings = new Dictionary<ConditionOperator, string>()
            {
                [ConditionOperator.Contains] = "Contains",
                [ConditionOperator.EndsWith] = "Ends with",
                [ConditionOperator.Equals] = "Equals",
                [ConditionOperator.Greater] = "Greater",
                [ConditionOperator.GreaterOrEqual] = "Greater or equal",
                [ConditionOperator.Less] = "Less",
                [ConditionOperator.LessOrEqual] = "Less or equal",
                [ConditionOperator.StartsWith] = "Starts with"
            };
            m_choosableConditions = value;
            if (value != null && value.Any())
            {
                //Conditions = new Condition[value.Count()];
                //int i = 0;
                foreach (var condition in value)
                {
                    Conditions.Add(new Condition(condition, mappings[condition]));
                    //Conditions[i] = new Condition(condition, mappings[condition]);
                    //i++;
                }
            }
        }
    }

    public List<Condition> Conditions { get; private set; } = new List<Condition>();

    public object Value
    {
        get
        {
            return m_value;
        }
        set
        {
            m_value = value;
        }
    }

    private bool ShouldShowOperatorsComboBox
    {
        get
        {
            return ChoosableConditions != null && ChoosableConditions.Any();
        }
    }

    private int InputPosition
    {
        get
        {
            int i = 0;
            foreach (var filterControl in Filter.FilterControls)
            {
                if (filterControl == this)
                    return i;
                i++;
            }
            throw new Exception($"Input cannot be found for filter {Filter.Name} and control {Label}.");
        }
    }

    public SearchDescriptorFilterControl() { }

    private Grid GetGrid()
    {
        var grid = new Grid();

        grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = GridLength.Auto });
        if (ShouldShowOperatorsComboBox)
            grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = GridLength.Auto });
        grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = GridLength.Auto });

        return grid;
    }

    private Label GetLabel()
    {
        var label = new Label();

        label.Content = Label;
        if (LabelWidth.HasValue)
            label.Width = LabelWidth.Value;
        else
        {
            var margin = label.Margin;
            margin.Right += 5;
            label.Margin = margin;
        }
        label.HorizontalContentAlignment = LabelContentAlignment;
        Grid.SetColumn(label, 0);

        return label;
    }

    private ComboBox GetConditionsComboBox()
    {
        var comboBox = new ComboBox();

        comboBox.Height = 21;
        comboBox.Width = 75;
        var margin = comboBox.Margin;
        margin.Right += 5;
        comboBox.Margin = margin;
        comboBox.SetBinding(ItemsControl.ItemsSourceProperty, new Binding($"SearchDescriptor.SelectedFilter.FilterControls[{InputPosition}].Conditions"));
        comboBox.IsSynchronizedWithCurrentItem = true;
        comboBox.SelectedValuePath = nameof(Condition.ConditionOperator);
        comboBox.DisplayMemberPath = nameof(Condition.Text);
        comboBox.SetBinding(Selector.SelectedItemProperty, new Binding($"SearchDescriptor.SelectedFilter.FilterControls[{InputPosition}].SelectedCondition")
        {
            Mode = BindingMode.TwoWay,
            UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
        });
        Grid.SetColumn(comboBox, 1);

        return comboBox;
    }

    private TextBox GetTextInput()
    {
        var input = new TextBox();

        input.SetBinding(TextBox.TextProperty, new Binding($"SearchDescriptor.SelectedFilter.FilterControls[{InputPosition}].Value") { Delay = 1000, UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged });

        return input;
    }

    private DatePicker GetDateInput()
    {
        var input = new DatePicker();

        input.SetBinding(DatePicker.SelectedDateProperty, new Binding($"SearchDescriptor.SelectedFilter.FilterControls[{InputPosition}].Value") { Delay = 1000, UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged });

        return input;
    }

    public Grid GetInputFilter()
    {
        var grid = GetGrid();
        grid.Children.Add(GetLabel());

        //At the moment the column for filter input is 1 - immediately after the label.
        var inputColumn = 1;

        // If the collection has any element that means we must supply
        // condition combo box so the user can choose a condition operator.
        // For that we create another column.
        if (ShouldShowOperatorsComboBox)
        {
            var comboBox = GetConditionsComboBox();
            grid.Children.Add(comboBox);

            //Combo box comes after the label which means
            //input filter comes after the combo box so we
            //will increment the inputColumn value by 1.
            inputColumn++;
        }

        //Filter input.
        FrameworkElement inputFilter;
        if (Type == FilterType.Date)
            inputFilter = GetDateInput();
        else
            inputFilter = GetTextInput();
        inputFilter.Width = Width;

        Grid.SetColumn(inputFilter, inputColumn);
        grid.Children.Add(inputFilter);

        return grid;
    }
}

1 个答案:

答案 0 :(得分:1)

绑定到SelectedValuePath媒体资源时,您不需要SelectedItem。此外,由于您似乎在定义源属性的同一个类中创建了ComboBox,因此您也可以指定绑定的Source,如下所示:

private ComboBox GetConditionsComboBox()
{
    var comboBox = new ComboBox();

    comboBox.Height = 21;
    comboBox.Width = 75;
    var margin = comboBox.Margin;
    margin.Right += 5;
    comboBox.Margin = margin;
    comboBox.SetBinding(ItemsControl.ItemsSourceProperty, new Binding("Conditions") { Source = this });
    comboBox.IsSynchronizedWithCurrentItem = true;
    comboBox.DisplayMemberPath = nameof(Condition.Text);
    comboBox.SetBinding(Selector.SelectedItemProperty, new Binding("SelectedCondition")
    {
        Source = this,
        Mode = BindingMode.TwoWay,
        UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
    });
    Grid.SetColumn(comboBox, 1);

    return comboBox;
}