【问题标题】:Multiple Combo Boxes with shared Binding - Display error after first selection from box具有共享绑定的多个组合框 - 从框中首次选择后显示错误
【发布时间】:2016-07-12 12:49:09
【问题描述】:

最近几天我遇到了一个问题,我很困惑,希望有人帮助我解决这个问题。

我正在开发一个 WPF 应用程序,它在第一次运行时会提示用户手动将检测到的串行端口分配给任意“通道”,在整个应用程序和以后的界面中用于显示数据等。

其中一个关键特性是,一旦在组合框中分配了一个端口,就不能再在其他组合框中进行选择(使用 ComboBoxItem 类的 .IsEnabled 属性)。

我遇到的问题是,虽然最初一切正常 - 每个组合框都已设置,打开下一个会看到之前的选择变灰 - 如果我尝试返回组合框,我已经之前设置它显示一个空的下拉列表。看起来下拉菜单仍处于活动状态,但包含项目的窗口的大小未正确调整。

屏幕截图:

Items are successfully disabled in subsequent combo boxes

Returning to an already selected box results in a blank drop down (blue circle)

这是组合框的 XAML 代码:

<StackPanel Grid.Column="1" Name="ComboPanel" Margin="5, 20, 5, 5">
            <ComboBox Margin="0, 5, 0, 0" Width="100" Height="25" Name="cboxChannel0"  IsSynchronizedWithCurrentItem="false" ItemsSource="{Binding portCollectionItems, UpdateSourceTrigger=PropertyChanged}" SelectedIndex="0" DropDownOpened="CboxChannel0_DropDownOpened" DropDownClosed="CboxChannel0_DropDownClosed" />
            <ComboBox Margin="0, 5, 0, 0" Width="100" Height="25" Name="cboxChannel1"  IsSynchronizedWithCurrentItem="false" ItemsSource="{Binding portCollectionItems, UpdateSourceTrigger=PropertyChanged}" SelectedIndex="0" DropDownOpened="CboxChannel0_DropDownOpened" DropDownClosed="CboxChannel0_DropDownClosed" />
            <ComboBox Margin="0, 5, 0, 0" Width="100" Height="25" Name="cboxChannel2"  IsSynchronizedWithCurrentItem="false" ItemsSource="{Binding portCollectionItems, UpdateSourceTrigger=PropertyChanged}" SelectedIndex="0" DropDownOpened="CboxChannel0_DropDownOpened" DropDownClosed="CboxChannel0_DropDownClosed" />
            <ComboBox Margin="0, 5, 0, 0" Width="100" Height="25" Name="cboxChannel3"  IsSynchronizedWithCurrentItem="false" ItemsSource="{Binding portCollectionItems, UpdateSourceTrigger=PropertyChanged}" SelectedIndex="0" DropDownOpened="CboxChannel0_DropDownOpened" DropDownClosed="CboxChannel0_DropDownClosed" />
            <ComboBox Margin="0, 5, 0, 0" Width="100" Height="25" Name="cboxChannel4"  IsSynchronizedWithCurrentItem="false" ItemsSource="{Binding portCollectionItems, UpdateSourceTrigger=PropertyChanged}" SelectedIndex="0" DropDownOpened="CboxChannel0_DropDownOpened" DropDownClosed="CboxChannel0_DropDownClosed" />
            <ComboBox Margin="0, 5, 0, 0" Width="100" Height="25" Name="cboxChannel5"  IsSynchronizedWithCurrentItem="false" ItemsSource="{Binding portCollectionItems, UpdateSourceTrigger=PropertyChanged}" SelectedIndex="0" DropDownOpened="CboxChannel0_DropDownOpened" DropDownClosed="CboxChannel0_DropDownClosed" />
            <ComboBox Margin="0, 5, 0, 0" Width="100" Height="25" Name="cboxChannel6"  IsSynchronizedWithCurrentItem="false" ItemsSource="{Binding portCollectionItems, UpdateSourceTrigger=PropertyChanged}" SelectedIndex="0" DropDownOpened="CboxChannel0_DropDownOpened" DropDownClosed="CboxChannel0_DropDownClosed" />
            <ComboBox Margin="0, 5, 0, 0" Width="100" Height="25" Name="cboxChannel7"  IsSynchronizedWithCurrentItem="false" ItemsSource="{Binding portCollectionItems, UpdateSourceTrigger=PropertyChanged}" SelectedIndex="0" DropDownOpened="CboxChannel0_DropDownOpened" DropDownClosed="CboxChannel0_DropDownClosed" />
            <ComboBox Margin="0, 5, 0, 0" Width="100" Height="25" Name="cboxChannel8"  IsSynchronizedWithCurrentItem="false" ItemsSource="{Binding portCollectionItems, UpdateSourceTrigger=PropertyChanged}" SelectedIndex="0" DropDownOpened="CboxChannel0_DropDownOpened" DropDownClosed="CboxChannel0_DropDownClosed" />
            <ComboBox Margin="0, 5, 0, 0" Width="100" Height="25" Name="cboxChannel9"  IsSynchronizedWithCurrentItem="false" ItemsSource="{Binding portCollectionItems, UpdateSourceTrigger=PropertyChanged}" SelectedIndex="0" DropDownOpened="CboxChannel0_DropDownOpened" DropDownClosed="CboxChannel0_DropDownClosed" />
            <ComboBox Margin="0, 5, 0, 0" Width="100" Height="25" Name="cboxChannel10" IsSynchronizedWithCurrentItem="false" ItemsSource="{Binding portCollectionItems, UpdateSourceTrigger=PropertyChanged}" SelectedIndex="0" DropDownOpened="CboxChannel0_DropDownOpened" DropDownClosed="CboxChannel0_DropDownClosed" />
            <ComboBox Margin="0, 5, 0, 0" Width="100" Height="25" Name="cboxChannel11" IsSynchronizedWithCurrentItem="false" ItemsSource="{Binding portCollectionItems, UpdateSourceTrigger=PropertyChanged}" SelectedIndex="0" DropDownOpened="CboxChannel0_DropDownOpened" DropDownClosed="CboxChannel0_DropDownClosed" />
        </StackPanel>

这是与盒子相关的背后代码sn-ps:

public partial class PortWindow : Window, INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public ObservableCollection<ComboBoxItem> portCollectionItems { get; set; }
    public ComboBoxItem selectedItem;
    public bool serialPortsSet { get; set; }

    public ComboBoxItem SelectedItem 
    {
        get { return selectedItem; }
        set 
        { 
            if (selectedItem == value) 
                return;

            selectedItem = value;
            OnPropertyChanged("IsEnabled");
        }
    }
    public PortWindow()
    {
        InitializeComponent();
        DataContext    = this;
        serialPortsSet = false;
        portCollectionItems = new ObservableCollection<ComboBoxItem>();

        for (int i = 0; i < ActiveSerialPorts.DetectedPorts.Count(); i++)
        {
            if (i == 0) 
            {
                portCollectionItems.Add(new ComboBoxItem { Content = "<-Select->" });
            }

            portCollectionItems.Add(new ComboBoxItem { Content = ActiveSerialPorts.DetectedPorts[i] }); // Populates collection with a list of serial port names from another class 
        }
    }

    void CboxChannel0_DropDownOpened(object sender, EventArgs e)
    {
        ComboBox comboBox     = sender as ComboBox;
        string selectedString = comboBox.SelectionBoxItem as string;
        selectedItem          = comboBox.SelectedItem as ComboBoxItem;

        foreach (ComboBoxItem portItems in portCollectionItems) 
        {
            if (portItems.Content == selectedItem.Content) 
            {
                portItems.IsEnabled = true; //re-enables the previously disabled selection in case the assigned port needs changing
            }
        }
    }

    void CboxChannel0_DropDownClosed(object sender, EventArgs e)
    {
        ComboBox comboBox = sender as ComboBox;
        selectedItem      = comboBox.SelectedItem as ComboBoxItem;
        string itemString = selectedItem.Content.ToString();

        if (!itemString.Contains("<-Select->"))
        {
            foreach (ComboBoxItem portItems in portCollectionItems) 
            {
                if (portItems.Content == selectedItem.Content) 
                {
                    portItems.IsEnabled = false; // disables the selected item in the observable collection 
                    return;
                }
            }  
        }
    }

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

我倾向于相信在绑定方面我缺少一些小而重要的东西。我最初认为是更改共享集合中的属性导致了问题,但是在抑制处理程序中的所有代码并运行它之后,问题仍然存在。

任何帮助将不胜感激!

【问题讨论】:

  • 你不应该有 UI 元素的集合,除非你写了一个自定义控件。

标签: c# wpf xaml combobox


【解决方案1】:

您正在不同的 ComboBox 之间共享 ComboBoxItem。这些是视觉元素,你试图在不同的父母之间分享它们。我并不惊讶它会破裂。

您需要每个 ComboBox 具有不同的 ComboBoxItem 实例集合。您可以通过将您的项目公开为ObservableCollection&lt;String&gt; 并让每个 ComboBox 创建自己的 ComboBoxItems 来最简单地做到这一点。以与绑定现有集合相同的方式绑定集合;我认为声明和填充该集合几乎应该是您需要进行的唯一更改,除非您需要立即禁用 all 框的端口项,而不仅仅是选择它们的那个。您的不过,组合框上的SelectedItem 将是字符串,而不是`ComboBoxItem,因此这些循环的内容将不得不稍作改变。

当你禁用一个 ComboBoxItem 时,它自然只会被它所属的 ComboBox 禁用。如果您想为所有框禁用COM4,则必须循环执行此操作。

或者您可以多使用一点 MVVM:如果是我,我会在模板化的 ItemsControl 中创建一系列 ComboBox,绑定到具有 SelectedPort 属性的某个类的集合,并且我会为项目也是如此,只是一个简单的事情 String PortNamebool IsPortEnabled。我会在 XAML 中将 IsPortEnabled 绑定到 ComboBoxItem.IsEnabled。代码会少很多,但从概念上讲,与您现在所处的位置相比,这是一个很大的飞跃。如果你有兴趣,我们可以去那里。

【讨论】:

  • 嗯,你是绝对正确的。进行了快速更改,为每个创建单独的 ObservableCollections,看起来不错。不幸的是,需要为所有组合框禁用禁用的选择,因为通过为一个通道选择一个,它不再是任何其他组合框的选项。如果项目对时间不那么敏感,我很乐意为您提供后一个建议的帮助 - 我对使用比 Excel VB 更复杂的语言进行编程还很陌生。有没有教程可以指点我以供将来参考?
  • @PSetters 我一直想写一个。我还没有找到我喜欢的。几年前,我从 Pluralsight Silverlight 课程(雇主付费)中获得了基础知识。我的女朋友对 Lynda.com 的 PHP 和 Web 内容评价很高,LinkedIn 最近向我发送了 lynda.com 一个月免费会员资格的垃圾邮件,不确定这是否仍然有效。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-20
  • 1970-01-01
  • 1970-01-01
  • 2012-09-01
  • 1970-01-01
相关资源
最近更新 更多