我有这个组合框:
<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);
}
}
}
在启动时,我只有一个项目:浏览... 。选择它我可以浏览文件并将其路径添加到ComboBox。 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:
<ComboBox SelectedItem="{Binding SelectedItem}" ItemsSource="{Binding Items}"/>
尝试:
- 选择FILE_A.txt
- 选择FILE_B.txt
- 再次选择FILE_A.txt
答案 0 :(得分: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>
在我的情况下,每次都会更改值,但您应该能够修改代码以满足您的需求。
确保您拥有清晰的代码结构,并且不会使事情过于复杂。
如果您想要更具体的答案,您应该考虑向您展示整个代码。
答案 1 :(得分: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
设置器从将其设置为{的调用中退出之前更新SelectedItem
{1}}。
因此ASD
中的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
这看起来更奇怪,但由于它不依赖于线程计时,它可能是一个更好的解决方案。另一方面,这是“viewmodel”代码,其细节由private void AddItem(string item)
{
// Harmless, if it's not actually there.
Items.Remove(item);
Items.Add(item);
SelectedItem = item;
}
控件的实现的特殊性驱动。那不是个好主意。
这应该可以在视图中完成(除了在这个人为的例子中我们的视图是我们的视图模型)。