2017-06-21 72 views
-1

更新我有这样的组合框:WPF组合框的SelectedItem绑定不从码

<ComboBox Grid.Column="1" SelectedItem="{Binding SelectedItem}" ItemsSource="{Binding Items, Mode=OneWay}" HorizontalAlignment="Stretch" VerticalAlignment="Center"/> 

,这是代码:

public class CustomComboBoxViewModel 
    { 
    private bool DiscardSelChanged { get; set; } 
    public ObservableCollection<string> Items { get; set; } 

    public string SelectedItem 
    { 
     get { return _selectedItem; } 
     set 
     { 
      if (!DiscardSelChanged) 
       _selectedItem = value; 
      bool old = DiscardSelChanged; 
      DiscardSelChanged = false; 
      if (!old) 
       SelectionChanged?.Invoke(_selectedItem); 
     } 
    } 

    public event Action<string> SelectionChanged; 

    public void AddItem(string item) 
    { 
     var v = Items.Where(x => x.Equals(item)).FirstOrDefault(); 
     if (v != default(string)) 
     { 
      SelectedItem = v; 
     } 
     else 
     { 
      DiscardSelChanged = true; 
      _selectedItem = item; 
      Items.Insert(0, item); 
     } 
    } 
} 

在启动的时候,我只有一个项目:浏览.. 。。选择它我可以浏览文件并将其路径添加到组合框。 AddItem方法被称为
如果选定的文件路径不存在项目我添加并选择它(这是工作)。
如果选择的文件路径存在于项目我想要自动选择它,而无需再次将其添加到列表中。这是行不通的,浏览...是可视化的项目。我已经尝试使用INotifyPropertyChanged
我使用.NET 4.6.2。任何想法让它工作?

编辑4:准系统例如

using System.Collections.ObjectModel; 
using System.ComponentModel; 
using System.Linq; 
using System.Windows; 

namespace WpfApp2 
{ 
    /// <summary> 
    /// Interaction logic for MainWindow.xaml 
    /// </summary> 
    public partial class MainWindow : Window, INotifyPropertyChanged 
    { 
     public MainWindow() 
     { 
      InitializeComponent(); 
      DataContext = this; 

      Items = new ObservableCollection<string>(); 
      Items.Add(ASD); 
     } 
     private string ASD = @"BROWSE"; 
     private string _selectedItem; 

     public string SelectedItem 
     { 
      get { return _selectedItem; } 
      set 
      { 
       _selectedItem = value; 
       OnPropertyChanged(nameof(SelectedItem)); 
       UploadFileSelection_SelectionChanged(); 
      } 
     } 
     public ObservableCollection<string> Items { get; set; } 

     public event PropertyChangedEventHandler PropertyChanged; 
     protected virtual void OnPropertyChanged(string propertyName) => 
      PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 

     private void AddItem(string item) 
     { 
      var v = Items.Where(x => x.Equals(item)).FirstOrDefault(); 
      if (v != default(string)) 
       SelectedItem = v; 
      else 
      { 
       Items.Add(item); 
       SelectedItem = item; 
      } 
     } 

     private void UploadFileSelection_SelectionChanged() 
     { 
      if (SelectedItem == ASD) 
      { 
       Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog() 
       { 
        DefaultExt = ".*", 
        Filter = "* Files (*.*)|*.*" 
       }; 
       bool? result = dlg.ShowDialog(); 

       if (result == true) 
        AddItem(dlg.FileName); 
      } 
     } 

    } 
} 

组合框:

<ComboBox SelectedItem="{Binding SelectedItem}" ItemsSource="{Binding Items}"/> 

尝试:
- 选择FILE_A.txt
- 选择FILE_B.txt
- 再次选择FILE_A.txt

+1

*“我已经尝试使用INotifyPropertyChanged。” - - 你做错了。告诉我们你是如何做到的,我们会帮助你解决它。 –

+0

编辑问题 – rmbq

+0

你还写了'公共类CustomComboBoxViewModel:INotifyPropertyChanged'吗? – Clemens

回答

1

我试过你的例子。我固定重入问题(双浏览对话框)与标志:

private bool _browsing = false; 
private void UploadFileSelection_SelectionChanged() 
{ 
    if (_browsing) 
    { 
     return; 
    } 

    if (SelectedItem == ASD) 
    { 
     try 
     { 
      _browsing = true; 
      Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog() 
      { 
       DefaultExt = ".*", 
       Filter = "* Files (*.*)|*.*" 
      }; 
      bool? result = dlg.ShowDialog(); 

      if (result == true) 
       AddItem(dlg.FileName); 
     } 
     finally 
     { 
      _browsing = false; 
     } 
    } 
} 

这是穴居人的东西,但它的工作原理。

你有真正的问题是,UploadFileSelection_SelectionChanged()被调用,更新SelectedItem你从它设置为ASD通话退出SelectedItem二传手之前。

因此SelectedItem = v;AddItem()对组合框没有影响,因为组合框对PropertyChanged没有响应。

这将解决这个问题:

private void AddItem(string item) 
{ 
    var v = Items.FirstOrDefault(x => x.Equals(item)); 

    if (v != default(string)) 
    { 
     //SelectedItem = v; 
     Task.Run(() => SelectedItem = v); 
    } 
    else 
    { 
     Items.Add(item); 
     SelectedItem = item; 
    } 
} 

现在我们以后做。

但请注意,其他分支确实有效,其中item新增加到集合中。您也可以伪造它通过消除item,然后重新添加:

private void AddItem(string item) 
{ 
    // Harmless, if it's not actually there. 
    Items.Remove(item); 

    Items.Add(item); 
    SelectedItem = item; 
} 

这看起来怪异,但由于它不依赖于线程时间,它可能是一个更好的解决方案。另一方面,这是“viewmodel”代码,其细节由控制的实现的特性驱动。这不是一个好主意。

这应该可能在视图中完成(不考虑在这个人造的例子中我们的视图是我们的视图模型)。

1

您正在设置_ selectedItem之后不需要拨打OnPropertyChanged()。这就是为什么它不起作用。如果你想有一个清晰的代码解决方案考虑OnPropertyChanged()这样执行的财产:

int _example; 
public int Example 
{ 
    get 
    { 
     return _example; 
    } 
    set 
    { 
     _example = value; 
     OnPropertyChanged(nameof(Example); 
    } 
} 

您的代码将是不容易出错。

做它尽可能地简单:

public class ViewModel : INotifyPropertyChanged 
{ 
    public ObservableCollection<string> Strings { get; set; } 

    public ICommand AddAnotherStringCommand { get; set; } 

    string _selectedItem; 
    public string SelectedItem 
    { 
     get 
     { 
      return _selectedItem; 
     } 

     set 
     { 
      _selectedItem = value; 
      OnPropertyChanged(nameof(this.SelectedItem)); 
     } 
    } 

    public int counter { get; set; } = 1; 

    public ViewModel() 
    { 
     // RelayCommand from: https://stackoverflow.com/questions/22285866/why-relaycommand 
     this.AddAnotherStringCommand = new RelayCommand<object>(AddAnotherString); 
     this.Strings = new ObservableCollection<string>(); 
     this.Strings.Add("First item"); 
    } 

    private void AddAnotherString(object notUsed = null) 
    { 
     this.Strings.Add(counter.ToString()); 
     counter++; 
     this.SelectedItem = counter.ToString(); 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 

    protected virtual void OnPropertyChanged(string propertyName) 
    { 
     PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 
    } 
} 

主窗口:

<Window x:Class="Test.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:Test" 
     mc:Ignorable="d" 
     Title="MainWindow" Height="350" Width="525"> 
    <Window.DataContext> 
     <local:ViewModel x:Name="ViewModel" /> 
    </Window.DataContext> 
    <StackPanel> 
     <ComboBox ItemsSource="{Binding Strings}" SelectedItem="{Binding SelectedItem}"/> 
     <Button Content="Add another item" Command="{Binding AddAnotherStringCommand}" /> 
    </StackPanel> 
</Window> 

在我的情况下,值改为每一次,但你应该能够修改代码以适应您的需要。

请确保您有清晰的代码结构并且不要过分复杂。

如果你想要一个更具体的答案,你应该考虑给你整个代码。