问题描述
在过去几天遇到的一个问题上,我感到很困惑,希望得到一些帮助。
I've been stumped on an issue I've run across in the last few days now and would like some help figuring it out.
我正在开发一个WPF应用程序,该应用程序在首次运行时会提示用户手动将检测到的串行端口分配给任意通道,并在整个应用程序和以后的界面中使用它来显示数据等。
I'm developing a WPF application that on its first run will prompt the user to manually assign detected serial ports to arbitrary 'channels', used throughout the application and later interface for displaying data etc.
其中一项主要功能是在组合框中分配了端口后,便无法再在其他端口中进行选择(使用ComboBoxItem类的.IsEnabled属性)。
One of the key features is once a port has been assigned in a combo box, it is then no longer available for selection in the others (using the .IsEnabled Property of the ComboBoxItem Class).
我遇到的问题是,尽管最初一切正常,但每个组合框都已设置,打开下一个组合框会看到上一个选择呈灰色-如果我尝试返回到我之前设置的组合框,该组合框显示一个空的下拉列表。下拉菜单似乎仍处于活动状态,但包含项的窗口的尺寸未正确设置。
The issue I've run into is that while initially everything works fine - each combo box is set, opening the next sees the previous selection greyed out - if I attempt to go back to a combo box I've previously set it displays an empty drop down. It looks as if the drop-down is still active but that the window that contains the items hasn't been sized properly.
屏幕截图:
我的组合框XAML代码:
Here's my XAML code for the combo boxes:
<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>
以下是与框相关的代码片段:
And here's the behind code snippets relevant to the boxes:
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));
}
我倾向于相信有些小东西但很重要,但我缺少关于绑定。我最初以为是共享集合中的一个属性更改导致了问题,但是在禁止处理程序中的所有代码并运行它之后,问题仍然存在。
I'm inclined to believe there's something small but vital I'm missing with regards to the binding. I originally thought it was the altering of a property within a shared collection that was causing the problem, but after supressing all of the code in the handlers and running it the issue persisted.
任何帮助将不胜感激!
推荐答案
您正在不同的ComboBox之间共享ComboBoxItem。这些是视觉元素,您正在尝试与其他父母分享。我不会感到惊讶。
You're sharing ComboBoxItems among different ComboBoxes. Those are visual elements and you're trying to share them among different parents. I'm not surprised it breaks.
您需要每个ComboBox都有不同的ComboBoxItem实例集合。您可以通过将项目显示为 ObservableCollection< String>
并让每个ComboBox创建自己的ComboBoxItems来实现。以与绑定已有收藏集相同的方式绑定收藏集;我认为声明和填充该集合几乎应该是您需要进行的唯一更改,除非您需要立即禁用 all 框的端口项,而不是只禁用其中的端口项。不过,组合框上的 SelectedItem
将是字符串,而不是`ComboBoxItem,因此这些循环的胆量必须有所改变。
You need each ComboBox to have a distinct collection of ComboBoxItem instances. You can most simply do that by exposing your items as ObservableCollection<String>
and letting each ComboBox create its own ComboBoxItems. Bind the collection the same way you're binding the collection you've got; I think that declaring and populating that collection should almost be the only change you need to make, unless you need to disable port items for all boxes at once, rather than just the one they were selected in. Your SelectedItem
on the combobox will be the string, though, rather than `ComboBoxItem, so the guts of those loops will have to change a bit.
当您禁用ComboBoxItem时,自然仅对其所属的ComboBox禁用。如果要为所有框禁用 COM4
,则必须循环执行此操作。
When you disable a ComboBoxItem, it will be disabled only for the ComboBox it belongs to, naturally. If you want COM4
disabled for all boxes, you'll have to do that in a loop.
或者您可以再添加一些MVVM:如果是我,我将在模板化ItemsItem中创建一系列ComboBoxes,绑定到某个类的集合,该类具有SelectedPort属性,我也将为项目使用自定义类,这只是简单的事情,其中包含 String PortName
和 bool IsPortEnabled
。我将 IsPortEnabled
绑定到XAML中的 ComboBoxItem.IsEnabled
。代码会少很多,但是从概念上来说,这与您现在的位置相比有了很大的飞跃。如果您有兴趣,我们可以去那里。
Or you could go a little more MVVM: If this were me, I would create the series of ComboBoxes in a templated ItemsControl bound to a collection of some class that had a SelectedPort property, and I would use a custom class for the items as well, just a simple thing with String PortName
and bool IsPortEnabled
. I'd bind IsPortEnabled
to ComboBoxItem.IsEnabled
in the XAML. There'd be a lot less code, but it's a big jump conceptually from where you're at now. We can go there if you're interested.
这篇关于具有共享绑定的多个组合框-首次从框中选择后显示错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!