我需要一个带有两个值的组合框。第一个应具有自定义名称,而第二个应使用基础绑定对象的属性。这两项都是VM上的值,我能够成功绑定所有这些值。
XAML
<Window x:Class="StaticComboBox.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:local="clr-namespace:StaticComboBox"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance local:StaticUIVm}"
Title="MainWindow"
Height="450"
Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto"/>
<RowDefinition />
</Grid.RowDefinitions>
<ComboBox Grid.Row="1"
SelectedValuePath="Tag"
SelectedValue="{Binding SelectedValue, Mode=TwoWay}">
<ComboBox.Items>
<ComboBoxItem Content="Custom Display Text 111"
Tag="{Binding FirstValue}" />
<ComboBoxItem Content="{Binding SecondValue.Item2}"
Tag="{Binding SecondValue}" />
</ComboBox.Items>
</ComboBox>
</Grid>
</Window>
XAML.cs
using System.Windows;
namespace StaticComboBox
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new StaticUIVm();
}
}
}
StaticUIVm.cs
using StaticComboBox.Annotations;
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace StaticComboBox
{
public class StaticUIVm : INotifyPropertyChanged
{
public Tuple<long, string> FirstValue { get; set; }
public Tuple<long, string> SecondValue { get; set; }
private Tuple<long, string> _selectedValue;
public Tuple<long, string> SelectedValue
{
get { return _selectedValue; }
set
{
_selectedValue = value;
OnPropertyChanged();
}
}
public StaticUIVm()
{
FirstValue = new Tuple<long, string>(1, "Some Static Value");
SecondValue = new Tuple<long, string>(2, "Some Other Static Value");
SelectedValue = FirstValue;
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
我的问题是,尽管绑定对于项目正确工作并且显示正确,并且当我作为用户选择值时,组合框在初始化VM类时并未反映正确的选择。意思是,它没有选择FirstValue。这对我来说没有意义,因为引用应该完全相同,并且我已经确认在初始化过程中虚拟机上的值实际上在变化。我肯定在构造函数中初始化了值,并尊重它们并在加载时显示了这些值,因此我对这里要出错的地方有些困惑。
编辑
我已经接受了mm8的答案,但不得不对XAML进行一些其他调整,以使其能够按需运行。我需要能够根据项目的ID值(在运行时设置)触发自定义文本。因此,一个简单的DataTrigger无法正常工作,因此我不得不使用MultiBinding。当选择一个项目时,MultiBinding破坏了显示(如ComboBox.ItemTemplate not displaying selection properly中所述),因此我不得不将IsEditable设置为false。完整的组合框在下面。
<ComboBox Grid.Row="2"
Grid.Column="1"
IsEditable="False"
ItemsSource="{Binding ItemSource}"
SelectedItem="{Binding SelectedValue}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock>
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Text"
Value="{Binding Name}" />
<Style.Triggers>
<DataTrigger Value="True">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource LongEqualToLongMultiBindingDisplayConverter}">
<Binding Path="Id" />
<Binding Path="DataContext.FirstValue.Id" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type local:MyControl}}" />
</MultiBinding>
</DataTrigger.Binding>
<Setter Property="Text"
Value="Custom Display Text 111" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
此XAML与mm8答案的建议(建立一个运行时根据提供的两个值初始化的集合)相结合,可以解决问题。
答案 0 :(得分:1)
为什么不简单地从视图模型中公开可选项的集合?这是使用MVVM解决此问题的方法:
public class StaticUIVm : INotifyPropertyChanged
{
public Tuple<long, string> FirstValue { get; set; }
public Tuple<long, string> SecondValue { get; set; }
private Tuple<long, string> _selectedValue;
public Tuple<long, string> SelectedValue
{
get { return _selectedValue; }
set
{
_selectedValue = value;
OnPropertyChanged();
}
}
public IEnumerable<Tuple<long, string>> Values { get; }
public StaticUIVm()
{
FirstValue = new Tuple<long, string>(1, "Some Static Value");
SecondValue = new Tuple<long, string>(2, "Some Other Static Value");
Values = new Tuple<long, string>[2] { FirstValue, SecondValue };
SelectedValue = SecondValue;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
XAML:
<ComboBox x:Name="cmb" Grid.Row="1" ItemsSource="{Binding Values}"
SelectedItem="{Binding SelectedValue}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock>
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Text" Value="{Binding Item2}" />
<Style.Triggers>
<DataTrigger Binding="{Binding Item1}" Value="1">
<Setter Property="Text" Value="Custom Display Text 111" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
您甚至可以删除FirstValue
和SecondValue
属性。自定义文本在视图中定义,但实际选择的选项在视图模型中定义。
答案 1 :(得分:0)
主要编辑:
因此,当我第一次添加答案时,我试图使用您已经拥有的内容,而不是演示如何做。 WPF在某些方面既灵活又狭窄,我经常发现自己在解决问题。当使用其他方法完成时,您的问题实际上非常简单。我的许多程序都具有ResponseEntity<String>
控件,尽管我通常使用集合来填充,但可以通过在ComboBox
上使用辅助数据类来应用类似的原理。这将显着增加灵活性,并且更加健壮。
我添加了属性Tuple
并将其绑定到您的datacontext类中的SelectedIndex
。我也将int
更改为SelectedValue
,因为在此用例中它要优越得多。
SelectedItem
数据上下文类,数据类和扩展方法:
因此,我将您的属性更改事件移到了单独的类。我建议这样做,因为它可以重用。对于数据类尤其方便。现在,在构造函数中,我们设置选定的项目和选定的索引。
<ComboBox Grid.Row="1"
SelectedValuePath="Tag"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
SelectedIndex="{Binding SelectedIndex, Mode=TwoWay}">
<ComboBox.Items>
<ComboBoxItem Content="{Binding FirstValue.display}"
Tag="{Binding FirstValue}" />
<ComboBoxItem Content="{Binding SecondValue.display}"
Tag="{Binding SecondValue}" />
</ComboBox.Items>
</ComboBox>
您可能会问所有这些的原因...那么,这增加了灵活性,而不是“黑客”事情或通过数据触发器在XAML中增加了额外的复杂性。您正在使用易于操作的数据类纯粹是在逻辑之外工作。