当Itemssource发生改变时,如何防止WPF组合框清除选择?

4
我有一个名字文本框,一个姓氏文本框和一个显示名称的下拉框。显示名称是可编辑的,所以用户可以输入任何他们想要的内容。但是,下拉框应该显示一系列选项。
当任意一个文本框改变时,下拉框的项源就会更新。然而,如果用户之前选择了值,那么文本属性将被清空。但是,如果用户已经键入了一个值,文本属性仍然存在。如何防止文本值被清空?
这是我的代码:
<Window x:Class="MainWindow"
        x:Name="MainApp"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow"
        Height="350"
        Width="525">
    <Grid DataContext="{Binding ElementName=MainApp}">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="auto" />
            <RowDefinition Height="auto" />
            <RowDefinition Height="auto" />
        </Grid.RowDefinitions>
        <TextBox Grid.Row="0"
                 Text="{Binding FirstName}" />
        <TextBox Grid.Row="1"
                 Text="{Binding LastName}" />
        <ComboBox x:Name="MyCb" Grid.Column="1"
                  Grid.Row="2" 
                  Text="{Binding TheName}"
                  ItemsSource="{Binding NameOptions}"
                  IsEditable="True" />
    </Grid>
</Window>

Imports System.ComponentModel

Class MainWindow
    Implements INotifyPropertyChanged

    Public ReadOnly Property NameOptions As List(Of String)
        Get
            Dim result As New List(Of String)
            result.Add(String.Format("{0} {1}", FirstName, LastName))
            result.Add(String.Format("{1}, {0}", FirstName, LastName))
            result.Add(String.Format("{1}, {0} MI", FirstName, LastName))
            Return result
        End Get
    End Property

    Public Property TheName As String

    Private _firstName As String
    Public Property FirstName As String
        Get
            Return _firstName
        End Get
        Set(value As String)
            _firstName = value
            RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("FirstName"))
            RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("NameOptions"))
        End Set
    End Property

    Private _lastname As String
    Public Property LastName As String
        Get
            Return _lastname
        End Get
        Set(value As String)
            _lastname = value
            RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("LastName"))
            RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("NameOptions"))
        End Set
    End Property

    Public Event PropertyChanged(sender As Object, e As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged
End Class
2个回答

2

请看我在这里的回答:如何禁用ComboBox的Text属性与ItemsSource同步

public class ComboBox : System.Windows.Controls.ComboBox
{
    private bool ignore = false;
    protected override void OnSelectionChanged(SelectionChangedEventArgs e)
    {
        if (!ignore)
        {
            base.OnSelectionChanged(e);
        }
    }

    protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
    {
        ignore = true;
        try
        {
            base.OnItemsChanged(e);
        }
        finally
        {
            ignore = false;
        }
    }
}

0
在更新NameOptions之前,将MyCb.SelectedItem设置为null。SelectedItem优先于Text,当ItemsSource被更新时,它也会尝试更新与SelectedItem连接的Text。
public partial class MainWindow : Window, INotifyPropertyChanged
{
    public List<String> NameOptions 
    {
        get 
        {
            MyCb.SelectedItem = null;   // <--- THIS is the solution. just try it!
            List<String> result = new List<String>();    
            result.Add(String.Format("{0} {1}", FirstName, LastName));
            result.Add(String.Format("{1}, {0}", FirstName, LastName));
            result.Add(String.Format("{1}, {0} MI", FirstName, LastName));
            return result;
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public MainWindow()
    {
        InitializeComponent();            
    }

    private String _theName;
    public String TheName
    {
        get { return _theName; }
        set
        {
            _theName = value;
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs("TheName"));
            }
        }
    }

    private String _firstName;
    public String FirstName
    {
        get { return _firstName; }
        set { 
            _firstName = value; 
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs("NameOptions"));
                handler(this, new PropertyChangedEventArgs("FirstName"));
            }
        }
    }

    private String _lastName;
    public String LastName
    {
        get { return _lastName; }
        set { 
            _lastName = value; 
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs("NameOptions"));
                handler(this, new PropertyChangedEventArgs("LastName"));
            }
        }
    }
}

除非我理解错了,否则这个建议仍然会清除组合框中显示的值。感谢您的尝试,但看起来我的绑定是正确的,这就是组合框设计的工作方式。 - c_manboy
你的代码不太像教科书上的,因为你在另一个属性中引发了组合框列表的属性更改通知。你认为这个解决方案为什么仍然可以清除代码?你甚至没有尝试过吗?我能够重现你描述的行为,并发现如果你从列表中从未选择项目并在编辑字段中输入自由格式名称,则更改名字不会损害组合框编辑字段的内容。但是一旦你选择了一个项目,它将覆盖编辑字段的内容——即使正在重新构建ItemsSource。附上我的C#代码。 - thomiel
离开了一段时间...在发布之前我尝试了你的建议,并再次尝试以确保。仍然不起作用。由于这种行为似乎是设计上的,我本来想将你的第一个答案标记为答案,但我再也没有看到它了。 - c_manboy
我删除了我的第一个答案,因为它并没有提供太多帮助。奇怪的是,你无法让我的代码正常工作。也许ComboBox的行为在新版本中发生了改变。我在这里使用的是VS 2012 Express,并且它完美地展示了你描述的问题以及我的解决方案。 - thomiel
顶一下:我也遇到了同样的问题。 - Mark A. Donohoe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接