2017-06-21 62 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> 

在我的情況下,值改爲每一次,但你應該能夠修改代碼以適應您的需要。

請確保您有清晰的代碼結構並且不要過分複雜。

如果你想要一個更具體的答案,你應該考慮給你整個代碼。