【问题标题】:SelectedValue not updating when set in code在代码中设置时 SelectedValue 不更新
【发布时间】:2016-12-21 01:16:28
【问题描述】:

我可以看到以前有人问过这个问题,但似乎没有什么对我有用。

我有一个 wpf 桌面应用程序。

我有这个组合框:

<ComboBox ItemsSource="{Binding Users, Mode=TwoWay,  UpdateSourceTrigger=PropertyChanged}" DisplayMemberPath="Value.Login"
   SelectedValue="{Binding SelectedManagerUser, 
   Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"  
   SelectedValuePath="Value"  
   IsSynchronizedWithCurrentItem="True"  />

数据源是一个字典对象:

public Dictionary<string,UserRecord> Users
{
    get
    {
       //get data
    }
    set { _Users =  value; RaisePropertyChanged(Constants.VM_Users); }
}

我在我的 MVVM 中添加一个新条目并更新数据。

然后我在我的 mvvm 中设置所选项目:

private UserRecord _selectedManagerUser;
public UserRecord SelectedManagerUser 
{ 
    get
    {
        return _selectedManagerUser;
    }
    set
    {
        _selectedManagerUser = value;
        RaisePropertyChanged("SelectedManagerUser");
    }
}
SelectedManagerUser = Users[temp];

public class UserRecord : ViewModelBase
{
    private int _Active;
    private int _UserRecordId;
    private string _UserRef;
    private string _FName;
    private string _SName;
    private string _Login;
    private string _Salt;
    private int _IsAdmin;
    private string _FullName;
    private string _Branch;
    private string _Position;
    private string _Department;

    public int Disabled { get { return _Active; } set { _Active = value; RaisePropertyChanged(InformedWorkerCommon.Constants.VM_Active); } }
    public int UserRecordId { get { return _UserRecordId; } set { _UserRecordId = value; RaisePropertyChanged("UserRecordId"); } }
    public string UserRef { get { return _UserRef; } set { _UserRef = value; RaisePropertyChanged(InformedWorkerCommon.Constants.VM_UserRef); } }
    public string FName { get { return _FName; } set { _FName = value; RaisePropertyChanged(InformedWorkerCommon.Constants.VM_FName); } }
    public string SName { get { return _SName; } set { _SName = value; RaisePropertyChanged(InformedWorkerCommon.Constants.VM_SName); } }
    public string Login { get { return _Login; } set { _Login = value; RaisePropertyChanged(InformedWorkerCommon.Constants.VM_Login); } }
    public string Salt { get { return _Salt; } set { _Salt = value; RaisePropertyChanged(InformedWorkerCommon.Constants.VM_Salt); } }
    public int IsAdmin { get { return _IsAdmin; } set { _IsAdmin = value; RaisePropertyChanged(InformedWorkerCommon.Constants.VM_IsAdmin); } }
    public string Branch { get { return _Branch; } set { _Branch = value; RaisePropertyChanged(InformedWorkerCommon.Constants.VM_Branch); } }
    public string Position { get { return _Position; } set { _Position = value; RaisePropertyChanged(InformedWorkerCommon.Constants.VM_Position); } }
    public string Department { get { return _Department; } set { _Department = value; RaisePropertyChanged(InformedWorkerCommon.Constants.VM_Department); } }
    public string FullName { get { return FName + ", " + SName; } set { _FullName = value; RaisePropertyChanged(InformedWorkerCommon.Constants.VM_Fullname); } }
}

我知道新项目已添加,因为 -

  1. 我可以在下拉菜单中看到它
  2. 我在代码中设置断点并检查。

组合框只显示一个空值。

还有什么我可以尝试的吗?

谢谢

【问题讨论】:

  • 什么是temp
  • 嗨,抱歉,是的,temp 是我创建的新密钥,是从文本框派生的。同样,密钥是有效的
  • 我猜问题是您的新键/值不在用户集合中。您必须记住,只有在 Users 中设置了新集合时,组合框才会收到有关用户集合中任何新项目的通知
  • @Pikoh 就像我在我的问题中所说的那样,它已被填充,这就是我发布问题的原因
  • 酷,现在有助于了解问题的全貌。我认为您的 IsSynchronizedWithCurrentItem 可能会因绑定到字典而感到困惑。尝试删除它以查看您的新条目是否出现。这可能就足够了,尽管您可能需要采用非字典式的方法。

标签: c# wpf mvvm


【解决方案1】:

不确定您那边出了什么问题,但看看可行的解决方案可能会有所帮助。

XAML:

<ComboBox ItemsSource="{Binding Users}"
          DisplayMemberPath="Value.Name"
          SelectedValue="{Binding SelectedUser}"  
          SelectedValuePath="Value" />

后面的代码:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        Loaded += WindowLoaded;

        var vm = new ViewModel();
        vm.Users.Add("u1", new UserRecord { Name = "User 1" });
        vm.Users.Add("u2", new UserRecord { Name = "User 2" });
        vm.Users.Add("u3", new UserRecord { Name = "User 3" });
        DataContext = vm;
    }

    private void WindowLoaded(object sender, RoutedEventArgs e)
    {
        // make sure it works after DataContext was set
        var vm = (ViewModel)DataContext;
        vm.SelectedUser = vm.Users["u2"];
    }
}

public class UserRecord
{
    public string Name { get; set; }
}

public class ViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public Dictionary<string, UserRecord> Users { get; }
        = new Dictionary<string, UserRecord>();

    private UserRecord selectedUser;
    public UserRecord SelectedUser
    {
        get { return selectedUser; }
        set
        {
            selectedUser = value;
            PropertyChanged?.Invoke(this,
                new PropertyChangedEventArgs(nameof(SelectedUser)));
        }
    }
}

【讨论】:

  • 感谢必须弹出,但回来时会弹出
  • 嗨,我回来了。现在看看这个。我将采用您的代码并重新处理我的东西。我一定是做错了什么。
  • 这确实对我有用,但不适用于当 UserRecord 具有许多不同的属性而不是您拥有的一个字段时。我设法通过绑定选定的索引并使用它来选择它来解决问题.
  • 嗨,是的,我倾向于同意你的观点。但出于某种原因,它确实如此。我的代码很简单
  • @AndrewSimpson 我已经在我的示例中尝试了 UserRecord 中的 3 个属性(2 个字符串,1 个整数)。它也完美无瑕..
【解决方案2】:

您的 SelectedManagerUser 属性应更改为此。 SelectedManagerUser 属性设置了新值,但您没有引发该事件,因此不会更新 UI。

    private UserRecord _selectedManagerUser;
    public UserRecord SelectedManagerUser 
    { 
        get
        {
            return _selectedManagerUser;
        }
        set
        {
            _selectedManagerUser = value;
            RaisePropertyChanged("SelectedManagerUser");
        }
    }

【讨论】:

  • 金,谢谢。我刚刚为该属性发布的 cod ei 是我的错。我有你有的。不过谢谢
  • 如果您的 .NET 版本不是太旧,RaisePropertyChanged(nameof(SelectedManagerUser)) 也可以使用
【解决方案3】:

Nuget 下载Prism 并从BindableBase 继承您的类。

使用后:

private UserRecord selectedManagerUser;

public UserRecord SelectedManagerUser
{
    get { return this.selectedManagerUser; }
    set { this.SetProperty(ref this.selectedManagerUser, value); }
}

【讨论】:

    【解决方案4】:

    有两种情况之一可能导致这种情况。首先,这可能是因为您没有将SelectedManagerUser 设置为不在字典中的UserRecord 的实例,或者字典仍然不适合数据绑定。让我把它们都盖上。

    当您使用 ItemsSourceSelectedItem 绑定时,如果您希望在 UI 中反映 SelectedItem 更改,您必须将其设置为可在 ItemsSource 中找到的实例。默认情况下,该控件将在源中查找引用等于所选项目的项目。我 99% 确定它会使用 IEquatable&lt;T&gt; 而不是参考检查您的项目是否实现了它。

    如果这不是你的问题,那是因为字典很适合数据绑定。

    字典对于数据绑定来说很糟糕。太可怕了。如果您需要一个键控集合并且想要对其进行绑定,请创建一个扩展 KeyedCollection 的自定义集合。通过一些额外的工作,TItem 可以实现 INPC(使密钥只读,tho)并且集合可以实现 INCC。非常适合装订。为什么我要提这个?继续阅读...

    您的问题是,在 ComboBox 中,SelectedItem 实际上是 KeyValuePair&lt;string,UserRecord&gt; 类型,而不是 UserRecord。所以绑定将不起作用。如果您获取 Snoop 的副本并在运行时检查绑定,您会看到这一点。

    问题是控件不知道 jack squat 的字典。它只知道IEnumerable&lt;T&gt;Dictionary&lt;K,T&gt; 实现了IEnumerable&lt;KeyValuePair&lt;K,T&gt;&gt;,因此控件为每个键值对创建一个项。 SelectedItem 也是一个键值对。因此,当您将其绑定到 UserRecord 类型的属性时,是的,它能够使用 SelectedValuePath 正确设置值,但它不能(不)[忍者编辑:除非这种行为已经改变过去几年:/] 迭代可枚举,以便在您在视图模型中设置值时找到正确的键值对

    如果UserRecord 的键值是该类型中的一个属性,那么一定要为它创建一个KeyedCollection。 KeyedCollection&lt;Tkey,TItem&gt; 实现了“IEnumerable”,因此它可以与绑定无缝协作。如果没有,请将其包装在代理中,或添加属性。

    当我说“将它包装在代理中”时,任何人都会说“什么,比如 KeyValuePair?”我要通过互联网打击你。代理成为您绑定的值。不要在SelectedValuePath 废话上浪费时间。直接与代理合作。当您需要您的值时,请在最后一刻提取它,而不是在绑定执行后立即提取。

    【讨论】:

    • 恐怕我不能同意这一点。看看@Clemens 的答案(我自己也有一个类似的例子),绑定工作得很好。
    • @Pikoh 嗯,我没有在很多框架版本中使用它们,所以它们可能改变了关于字典的 ItemsControl 行为。如果是这样的话,那可能是因为他没有正确处理他的推荐信。编辑以包括另一种可能性。谢谢。
    • 您必须记住,OP 不是使用SelectedItem,而是使用SelectedValueSelectedValuePath=value。这使得SelectedManagerUser 的类型为UserRecord
    • @Pikoh 是的,我知道,但这很可能是他的问题的一部分。我没有代码(我确实尖叫),因为我还没有创建一个原型来重新检查字典的数据绑定。但是这种方法(SelectedValuePath 等)不必要地复杂,并且更容易出现数据绑定问题(正如 OP 充分说明的那样)。这基本上就是我的意思 不要浪费你的时间在这个 SelectedValuePath 废话上
    • @Pikoh 全新品牌。 提示“一个全新的世界”
    猜你喜欢
    • 2017-01-28
    • 1970-01-01
    • 2011-06-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多