我有一个带有DataTemplate的Listbox,其中包含一个Combobox。我需要更改特定ComboBox的selectedItem / Index。我该如何访问它?
其他详细信息
所有组合框都有相同的选项。如果将Combobox设置为与另一个ComboBox相同的值,则首先设置的Combobox应该返回空(这是我的cbxOptions
字典中ComboBoxes绑定的第一个项目。)
<DataTemplate x:Key="lbxHeaderDataTemplate">
<Grid>
<Label Content="{Binding Item1}"></Label>
<ComboBox Grid.Column="1" ItemsSource="{Binding Item2}"
DisplayMemberPath="Key" SelectionChanged="ComboBox_SelectionChanged"></ComboBox>
</Grid>
</DataTemplate>
C# 填充用户界面
foreach (DataColumn dc in _loadedData.Columns)
{
ListBox.Items.Add(new Tuple<string, Dictionary<string, string>>
(dc.ColumnName, cbxOptions));
}
尝试擦除组合框
这是我期望我可以通过列表框进行预测的地方,检查控件是否匹配,此时我将其更改为空白。然而,我的foreach只是让我回到了愚蠢的元组......这是只读的,但我认为不管怎么说都要更新我的ComboBox。
private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ComboBox cbxSelected = (ComboBox)sender;
DependencyObject parent = VisualTreeHelper.GetParent(cbxSelected);
Label currentLbl = null;
foreach (object o in ((Grid)parent).Children)
{
if (o is Label)
{
currentLbl = (Label)o;
}
}
string LblText = currentLbl.Content.ToString();
string cbxValue = cbxSelected.SelectedValue.ToString();
//HERE I want to iterate through the listbox controls, not the datasource
foreach (Tuple<string, Dictionary<string, string>> l in lbxDatFields.Items)
{
//l.Item2 = "";
if (l.Item1.EndsWith(cbxOptions[cbxValue]))
l = new Tuple<string, Dictionary<string, string>>(l.Item1, "");
}
}
我确定必须有一种非常简单的方法来访问控件。任何帮助将非常感激。如果需要其他信息,请告诉我。
答案 0 :(得分:1)
如果没有一个好的Minimal, Complete, and Verifiable code example能够清晰简洁地说明您的问题,那么尝试解决您当前的设计是不切实际的。根据您发布的一些代码,可以做一些观察:
Tuple<...>
是不可变的,因此您无法修改Item2
属性。您必须用新的Tuple<...>
对象替换整个l
对象。foreach
循环中的Item2
变量。ComboBox
选项。 ComboBox
项的字典对象的使用使我无法使用。也许有一个完整的代码示例,它会更清楚。
所有这一切......
我如何访问它?
这个问题的出现只是因为你误用了WPF。你不应该直接操纵UI;相反,您的UI状态应该在视图模型数据结构中表示。然后class PropertyChangedExEventArgs<T> : PropertyChangedEventArgs
{
public T OldValue { get; }
public PropertyChangedExEventArgs(string propertyName, T oldValue)
: base(propertyName)
{
OldValue = oldValue;
}
}
class ItemViewModel : INotifyPropertyChanged
{
private string _name;
public string Name
{
get { return _name; }
set { _UpdateField(ref _name, value); }
}
private string _value;
public string Value
{
get { return _value; }
set { _UpdateField(ref _value, value); }
}
public event PropertyChangedEventHandler PropertyChanged;
protected void _UpdateField<T>(ref T field, T newValue,
Action<T> onChangedCallback = null,
[CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, newValue))
{
return;
}
T oldValue = field;
field = newValue;
onChangedCallback?.Invoke(oldValue);
PropertyChanged?.Invoke(this,
new PropertyChangedExEventArgs<T>(propertyName, oldValue));
}
}
选择将绑定到视图模型属性,您问题的答案就是查看该属性。
由于缺乏细节,很难确定,但在我看来,您正在尝试实施一个项目列表,其中每个项目都有一个可选项,并且你希望这些选项是互斥的。也就是说,一次只能有一个项目可以有任何给定的选项。
假设情况如此,我将展示一个实现,在我看来,它比你试图实现的方法要好得多。也就是说,它使用我上面提出的基本思想,从数据模型开始,然后从那里回到UI。这样做,数据模型非常简单易懂,所有行为的实现也是如此。
看起来像这样......
首先,从基本的每项视图模型数据结构开始:
INotifyPropertyChanged
注意:
Values
。在实际程序中,此实现通常位于基类中,每个视图模型类都继承该基类。如果您进行了大量的WPF编程,那么您将此基类作为可重用组件包含在每个项目中,可以在您引用的单独项目中,也可以作为代码片段。您可以使用许多WPF用户框架,它们提供此功能。PropertyChangedEventArgs
集合进行线性搜索。但我决定扩展class MainViewModel
{
public ObservableCollection<ItemViewModel> Items { get; } =
new ObservableCollection<ItemViewModel>
{
new ItemViewModel { Name = "Item #1" },
new ItemViewModel { Name = "Item #2" },
new ItemViewModel { Name = "Item #3" },
};
public IReadOnlyList<string> Options { get; } =
new [] { "Option One", "Option Two", "Option Three" };
private readonly Dictionary<string, ItemViewModel> _valueToModel =
new Dictionary<string, ItemViewModel>();
public MainViewModel()
{
foreach (ItemViewModel itemModel in Items)
{
itemModel.PropertyChanged += _ItemPropertyChanged;
}
}
private void _ItemPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(ItemViewModel.Value))
{
ItemViewModel itemModel = (ItemViewModel)sender;
PropertyChangedExEventArgs<string> exArgs =
(PropertyChangedExEventArgs<string>)e;
if (exArgs.OldValue != null)
{
_valueToModel.Remove(exArgs.OldValue);
}
if (itemModel.Value != null)
{
if (_valueToModel.TryGetValue(
itemModel.Value, out ItemViewModel otherModel))
{
otherModel.Value = null;
}
_valueToModel[itemModel.Value] = itemModel;
}
}
}
}
类,因为它是针对该特定需求的更具可扩展性的解决方案(因此作为该问题的通用解决方案更有用)。 / LI>
在这里,我只需要一个类来实现该接口,为了便于说明,将其保持在一起更简单。
好的,所以使用per-item数据结构,我们还希望父数据结构将这些项封装为集合,并处理对这些项的更广泛操作:
ComboBox
此对象维护项目集合以及PropertyChanged
元素的选项集合。这也是处理处理选项互斥的逻辑的地方,因为这是可以访问所有每个项目数据对象的类。
关于最后一点:当然,您可以为每个项目对象提供一种与父数据结构交互的方法,以便能够枚举其他每个项目对象。这将允许每个每个项目对象处理其自己的属性更改,以便父对象不需要订阅每个项目对象的<Window x:Class="TestSO45196940ComboBoxExclusive.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:l="clr-namespace:TestSO45196940ComboBoxExclusive"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<l:MainViewModel/>
</Window.DataContext>
<StackPanel>
<ListBox ItemsSource="{Binding Items}">
<ListBox.Resources>
<DataTemplate DataType="{x:Type l:ItemViewModel}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Content="{Binding Name}"/>
<ComboBox ItemsSource="{Binding DataContext.Options, RelativeSource={RelativeSource AncestorType=ListBox}}"
SelectedItem="{Binding Value}" Grid.Column="1"/>
</Grid>
</DataTemplate>
</ListBox.Resources>
</ListBox>
</StackPanel>
</Window>
事件。但这样做也会增加类之间的耦合,使基本逻辑更难遵循。恕我直言,最好保持这种自上而下的方法,即拥有的对象尽可能少地了解它们的所有者(在这种情况下,根本没有任何东西)。
注意,通过上述内容,可以使用跟踪项目状态并确保互斥选项设置所需的所有逻辑,而不存在实际上特定于视图对象的任何。上述代码可以在任何程序中使用,无论是否有用户界面。它完全脱离了视角本身。
那么,视图如何使用它?像这样:
ItemViewModel
类似于MainViewModel
对象对PropertyChanged
一无所知,而后者订阅前INotifyPropertyChanged
事件并访问项目对象&#39}要完成工作的属性,视图会绑定到两个模型对象的相关属性,而这些对象无需了解这些绑定或视图本身。
该视图根本没有代码隐藏。它只是对用户所看到内容的简单,声明性描述,并且只是向用户呈现基础数据的当前状态。
这样做可以使一切变得非常简单和断开,因此每个对象都有一组非常狭窄的责任,并且对象之间的交互保持最小。这样可以更容易地确保代码是正确的,并在实现功能时减少心理工作量,因为您一次只处理一小部分代码,而不是必须保持一切与彼此。
对于它的价值,在这篇文章中解释上面的代码需要更长的时间方式,而不是编写代码本身。按照标准的WPF惯用法,实际创作代码的速度非常快,特别是如果你已经为{{1}}之类的东西准备了基本的基类。大部分时间的节省来自于不必考虑如何获取所需的数据。通过遵循更好的实践,数据总是在您想要的地方。
答案 1 :(得分:1)
我有一个带有DataTemplate的Listbox,其中包含一个Combobox。我需要更改特定ComboBox的selectedItem / Index。我该如何访问它?
访问Items
。{/ p>的ListBox
集合中的相应数据项
将您的Tuple<string, Dictionary<string, string>>
替换为包含SelectedIndex
属性的类。确保正确实现INotifyPropertyChanged
接口:
class DataItem : INotifyPropertyChanged
{
public string Item1 { get; set; }
public Dictionary<string, string> Item2 { get; set; }
private int _selectedIndex;
public int SelectedIndex
{
get { return _selectedIndex; }
set { _selectedIndex = value; OnPropertyChanged(); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
...
foreach (DataColumn dc in _loadedData.Columns)
{
ListBox.Items.Add(new DataItem() { Item1 = dc.ColumnName, Item2 = cbxOptions });
}
然后,将SelectedIndex
中ComboBox
的{{1}}属性绑定到DataTemplate
属性:
SelectedIndex
通过在<ComboBox Grid.Column="1" ItemsSource="{Binding Item2}" DisplayMemberPath="Key"
SelectedIndex="{Binding SelectedIndex}"></ComboBox>
集合中设置相应对象的source属性来更改ComboBox
的所选索引:
Items