由于版本的原因,我无法运行您的测试项目,但我将ViewModel
类和 XAML 复制到了一个新的测试项目,这一切似乎都按预期工作,选定的项目被清除,组合框禁用,并且水印出现时按钮被点击。
所以我猜你的环境中的某些东西正在影响它,或者它是你的代码中没有立即显现的其他东西。
为了找出哪个,我在下面发布了我用来测试的(正确工作的)代码。
如果这不起作用,那么问题与您的环境有关。我正在使用 VS2010、.Net 4.0 和 Windows 7。
如果是这样,那么您的代码中的其他地方肯定发生了一些在您的测试示例中没有立即显现的东西。我会检查任何异步代码以查看 PropertyChange 通知是否发生在后台线程上,因为有时这不会触发 UI 线程上的更新。
对于由此产生的代码转储,我提前道歉:)
该类ViewModel
是从测试项目中复制的,并进行了查找/替换以更改"BrowserInstance"
为“ViewModelBase”以适合我的测试应用程序。一些不必要的部分也被注释掉或稍微修改以适应我的测试应用程序。
public sealed class ViewModel : INotifyPropertyChanged
{
#region Members: Fields
private Boolean m_ButtonAttachEnabled;
private Boolean m_ButtonDetachEnabled;
private Boolean m_ButtonRefreshEnabled;
private Boolean m_ComboBoxEnabled;
private Boolean m_ProgressBarEnabled;
private ViewModelBase m_SelectedItem;
private Dictionary<String, PropertyChangedEventArgs> m_PropertyChangedEventArgs = new Dictionary<String, PropertyChangedEventArgs>();
private Double m_ProgressBarMaximum;
private Double m_ProgressBarValue;
private IntPtr m_ProcessHandle;
private IntPtr m_ProcessHook;
private ObservableCollection<ViewModelBase> m_Items;
private Visibility m_WatermarkVisibility;
#endregion
#region Members: INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
#endregion
#region Properties
public Boolean ButtonAttachEnabled
{
get { return m_ButtonAttachEnabled; }
private set
{
if (m_ButtonAttachEnabled != value)
{
m_ButtonAttachEnabled = value;
NotifyPropertyChanged("ButtonAttachEnabled");
}
}
}
public Boolean ButtonDetachEnabled
{
get { return m_ButtonDetachEnabled; }
private set
{
if (m_ButtonDetachEnabled != value)
{
m_ButtonDetachEnabled = value;
NotifyPropertyChanged("ButtonDetachEnabled");
}
}
}
public Boolean ButtonRefreshEnabled
{
get { return m_ButtonRefreshEnabled; }
private set
{
if (m_ButtonRefreshEnabled != value)
{
m_ButtonRefreshEnabled = value;
NotifyPropertyChanged("ButtonRefreshEnabled");
}
}
}
public Boolean ComboBoxEnabled
{
get { return m_ComboBoxEnabled; }
private set
{
if (m_ComboBoxEnabled != value)
{
m_ComboBoxEnabled = value;
NotifyPropertyChanged("ComboBoxEnabled");
}
}
}
public Boolean ProgressBarEnabled
{
get { return m_ProgressBarEnabled; }
private set
{
if (m_ProgressBarEnabled != value)
{
m_ProgressBarEnabled = value;
NotifyPropertyChanged("ProgressBarEnabled");
}
}
}
public ViewModelBase SelectedItem
{
get { return m_SelectedItem; }
set
{
if (m_SelectedItem != value)
{
m_SelectedItem = value;
NotifyPropertyChanged("SelectedItem");
}
}
}
public Double ProgressBarMaximum
{
get { return m_ProgressBarMaximum; }
private set
{
if (m_ProgressBarMaximum != value)
{
m_ProgressBarMaximum = value;
NotifyPropertyChanged("ProgressBarMaximum");
}
}
}
public Double ProgressBarValue
{
get { return m_ProgressBarValue; }
private set
{
if (m_ProgressBarValue != value)
{
m_ProgressBarValue = value;
NotifyPropertyChanged("ProgressBarValue");
}
}
}
public ObservableCollection<ViewModelBase> Items
{
get { return m_Items; }
private set
{
if (m_Items != value)
{
m_Items = value;
NotifyPropertyChanged("Items");
}
}
}
public Visibility WatermarkVisibility
{
get { return m_WatermarkVisibility; }
private set
{
if (m_WatermarkVisibility != value)
{
m_WatermarkVisibility = value;
NotifyPropertyChanged("WatermarkVisibility");
}
}
}
#endregion
#region Constructors
public ViewModel()
{
m_PropertyChangedEventArgs = new Dictionary<String, PropertyChangedEventArgs>();
Populate();
}
#endregion
#region Methods: Functions
private PropertyChangedEventArgs GetPropertyChangedEventArgs(String propertyName)
{
PropertyChangedEventArgs propertyChangedEventArgs;
if (!m_PropertyChangedEventArgs.TryGetValue(propertyName, out propertyChangedEventArgs))
m_PropertyChangedEventArgs[propertyName] = propertyChangedEventArgs = new PropertyChangedEventArgs(propertyName);
return propertyChangedEventArgs;
}
private void NotifyPropertyChanged(String propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, GetPropertyChangedEventArgs(propertyName));
}
private void ResetProgressBar(Int32 maximumValue = 100)
{
ProgressBarMaximum = maximumValue;
ProgressBarValue = 0;
}
private void SetInterfaceStatus(Status status)
{
switch (status)
{
case Status.Attached:
{
ButtonAttachEnabled = false;
ButtonDetachEnabled = true;
ButtonRefreshEnabled = false;
ComboBoxEnabled = false;
ProgressBarEnabled = false;
WatermarkVisibility = Visibility.Hidden;
break;
}
case Status.Busy:
{
ButtonAttachEnabled = false;
ButtonDetachEnabled = false;
ButtonRefreshEnabled = false;
ComboBoxEnabled = false;
ProgressBarEnabled = true;
WatermarkVisibility = Visibility.Hidden;
break;
}
case Status.Detached:
{
ButtonAttachEnabled = true;
ButtonDetachEnabled = false;
ButtonRefreshEnabled = true;
ComboBoxEnabled = true;
ProgressBarEnabled = true;
WatermarkVisibility = Visibility.Hidden;
goto default;
}
case Status.DetachedEmpty:
{
ButtonAttachEnabled = false;
ButtonDetachEnabled = false;
ButtonRefreshEnabled = true;
ComboBoxEnabled = false;
ProgressBarEnabled = false;
WatermarkVisibility = Visibility.Visible;
goto default;
}
default:
ResetProgressBar();
break;
}
}
public void Attach()
{
}
public void Detach()
{
}
public void Populate()
{
ViewModelBase selectedItem = m_SelectedItem;
SelectedItem = null;
Items = new ObservableCollection<ViewModelBase>();
List<ViewModelBase> ViewModelBases = new List<ViewModelBase>();
if (selectedItem == null)
{
ViewModelBases.Add(new Test { TestValue = "123456789", TestEnum = TestEnum.A, TestBool = false, TestBool2 = true });
ViewModelBases.Add(new Test { TestValue = "987365321", TestEnum = TestEnum.B, TestBool = true, TestBool2 = true });
ViewModelBases.Add(new Test { TestValue = "784512457", TestEnum = TestEnum.B, TestBool = true, TestBool2 = true });
}
//foreach (Process process in Process.GetProcesses())
//{
// if ((process.ProcessName == "chrome") && !process.HasExited && (process.MainModule.ModuleName == "chrome.exe"))
// ViewModelBases.Add(new ViewModelBase(ViewModelBaseType.Chrome, process));
//}
if (ViewModelBases.Count == 0)
SetInterfaceStatus(Status.DetachedEmpty);
else
{
Items = new ObservableCollection<ViewModelBase>(ViewModelBases);
if (selectedItem != null)
SelectedItem = m_Items.SingleOrDefault(x => x.Equals(selectedItem));
if (m_SelectedItem == null)
SelectedItem = m_Items[0];
SetInterfaceStatus(Status.Detached);
}
}
#endregion
#region Enumerators
private enum Status
{
Attached,
Busy,
Detached,
DetachedEmpty
}
#endregion
}
我的测试应用程序中的ViewModelBase
类如下所示:
public class ViewModelBase : INotifyPropertyChanged, IDataErrorInfo
{
// Fields
private PropertyChangedEventHandler propertyChanged;
// Events
public event PropertyChangedEventHandler PropertyChanged
{
add
{
PropertyChangedEventHandler handler2;
PropertyChangedEventHandler propertyChanged = this.propertyChanged;
do
{
handler2 = propertyChanged;
PropertyChangedEventHandler handler3 = (PropertyChangedEventHandler)Delegate.Combine(handler2, value);
propertyChanged = Interlocked.CompareExchange<PropertyChangedEventHandler>(ref this.propertyChanged, handler3, handler2);
}
while (propertyChanged != handler2);
}
remove
{
PropertyChangedEventHandler handler2;
PropertyChangedEventHandler propertyChanged = this.propertyChanged;
do
{
handler2 = propertyChanged;
PropertyChangedEventHandler handler3 = (PropertyChangedEventHandler)Delegate.Remove(handler2, value);
propertyChanged = Interlocked.CompareExchange<PropertyChangedEventHandler>(ref this.propertyChanged, handler3, handler2);
}
while (propertyChanged != handler2);
}
}
protected void RaisePropertyChanged(params string[] propertyNames)
{
if (propertyNames == null)
{
throw new ArgumentNullException("propertyNames");
}
foreach (string str in propertyNames)
{
this.RaisePropertyChanged(str);
}
}
protected void RaisePropertyChanged<T>(Expression<Func<T>> propertyExpression)
{
string propertyName = PropertySupport.ExtractPropertyName<T>(propertyExpression);
this.RaisePropertyChanged(propertyName);
}
protected virtual void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler propertyChanged = this.propertyChanged;
if (propertyChanged != null)
{
propertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#region IDataErrorInfo & Validation Members
/// <summary>
/// List of Property Names that should be validated
/// </summary>
//protected List<string> ValidatedProperties = new List<string>();
#region Validation Delegate
public delegate string ValidationDelegate(
object sender, string propertyName);
private List<ValidationDelegate> _validationDelegates =
new List<ValidationDelegate>();
public void AddValidationDelegate(ValidationDelegate func)
{
_validationDelegates.Add(func);
}
public void RemoveValidationDelegate(ValidationDelegate func)
{
if (_validationDelegates.Contains(func))
_validationDelegates.Remove(func);
}
#endregion // Validation Delegate
public virtual string GetValidationError(string propertyName)
{
// If user specified properties to validate, check to see if this one exists in the list
//if (ValidatedProperties.IndexOf(propertyName) < 0)
//{
// return null;
//}
string s = null;
//switch (propertyName)
//{
//}
foreach (var func in _validationDelegates)
{
s = func(this, propertyName);
if (s != null)
return s;
}
return s;
}
string IDataErrorInfo.Error { get { return null; } }
string IDataErrorInfo.this[string propertyName]
{
get { return this.GetValidationError(propertyName); }
}
//public bool IsValid
//{
// get
// {
// return (GetValidationError() == null);
// }
//}
//public string GetValidationError()
//{
// string error = null;
// if (ValidatedProperties != null)
// {
// foreach (string s in ValidatedProperties)
// {
// error = GetValidationError(s);
// if (error != null)
// {
// return error;
// }
// }
// }
// return error;
//}
#endregion // IDataErrorInfo & Validation Members
}
Test
测试类是这样的:
public class Test : ViewModelBase
{
public int Id { get; set; }
private TestEnum _testEnum;
private string _testValue = "Testing";
private double _testNumber = 10000;
private bool _testBool;
private bool _testBool2;
private ObservableCollection<int> _someCollection;
public ObservableCollection<int> SomeCollection
{
get
{
if (_someCollection == null)
_someCollection = new ObservableCollection<int>();
return _someCollection;
}
}
public Test()
{
//this.ValidatedProperties.Add("TestValue");
//this.ValidatedProperties.Add("TestNumber");
SomeCollection.Add(1);
SomeCollection.Add(2);
SomeCollection.Add(3);
}
public override string ToString()
{
return TestValue;
}
public void RaisePropertyChanged1(string property)
{
base.RaisePropertyChanged(property);
}
//public override string GetValidationError(string propertyName)
//{
// // If user specified properties to validate, check to see if this one exists in the list
// if (ValidatedProperties.IndexOf(propertyName) < 0)
// {
// return null;
// }
// string s = base.GetValidationError(propertyName); ;
// switch (propertyName)
// {
// case "TestValue":
// s = "error";
// break;
// }
// return s;
//}
public TestEnum TestEnum
{
get { return _testEnum; }
set
{
if (value != _testEnum)
{
_testEnum = value;
base.RaisePropertyChanged(() => this.TestEnum);
}
}
}
private ObservableCollection<int> _test;
public ObservableCollection<int> Test1
{
get
{
if (_test == null)
{
_test = new ObservableCollection<int>();
for (int i = 0; i < 5; i++)
{
_test.Add(i);
}
}
return _test;
}
set
{
if (value != _test)
{
_test = value;
RaisePropertyChanged(() => this.Test1);
}
}
}
public string TestValue
{
get { return _testValue; }
set
{
if (value != _testValue)
{
_testValue = value;
base.RaisePropertyChanged(() => this.TestValue);
}
}
}
public bool TestBool
{
get { return _testBool; }
set
{
if (value != _testBool)
{
_testBool = value;
base.RaisePropertyChanged(() => this.TestBool);
}
}
}
public bool TestBool2
{
get { return _testBool2; }
set
{
if (value != _testBool2)
{
_testBool2 = value;
base.RaisePropertyChanged(() => this.TestBool);
}
}
}
public double TestNumber
{
get { return _testNumber; }
set
{
if (value != _testNumber)
{
_testNumber = value;
base.RaisePropertyChanged(() => this.TestNumber);
}
}
}
}
我的测试 XAML 看起来像这样。我添加了一个最小高度,因为它在我的测试样式中没有正确显示,并且添加了背景颜色只是为了查看它的渲染位置。
<Grid MinHeight="100" Background="Lavender">
<Canvas Background="Red">
<GroupBox Canvas.Left="10" Canvas.Top="5" BorderBrush="Black" Header="Test" Height="86" Width="472">
<Canvas>
<ComboBox Canvas.Left="5" Canvas.Top="5" IsEnabled="{Binding Path=ComboBoxEnabled}" ItemsSource="{Binding Path=Items}" SelectedItem="{Binding Mode=TwoWay, Path=SelectedItem}" Width="366">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock MaxWidth="{Binding Path=ActualWidth, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ComboBox}}}" Text="{Binding }" TextTrimming="CharacterEllipsis"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<Label Canvas.Left="5" Canvas.Top="4" TextOptions.TextFormattingMode="Display" Content="This is a Test" Height="50" IsEnabled="False" Visibility="{Binding Path=WatermarkVisibility}" Width="366"/>
<Button Canvas.Right="5" Canvas.Top="5" Click="Button_Click_1" Content="Refresh" IsEnabled="{Binding Path=ButtonRefreshEnabled}" Width="75"/>
</Canvas>
</GroupBox>
</Canvas>
</Grid>
最后,我的 XAML 背后的代码:
ViewModel m_ViewModel;
public MainWindow()
{
InitializeComponent();
DataContext = m_ViewModel = new ViewModel();
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
m_ViewModel.Populate();
}