更改字符串类型的匿名 属性 的值
Change value for anonymous property of type string
我正在开发一个用 WPF 编写的应用程序,代码是用 C# 编写的。
我有一个问号图标,当按下它时假设将内容设置为特定标签。
标签内容绑定到视图模型中的 属性,我们称之为 'NoneLegend'。
我希望 属性 在 5 秒后自行清除,因此我有一个实用程序 class 可以管理它。在 class 里面,我写了一个匿名方法来获取任何类型的 属性。
我的问题是如何将 属性 设置为 string.empty?
该方法如下所示:
public static void EmptyStringAfterXseconds<T>(Expression<Func<T>> property)
{
var propertyInfo = ((MemberExpression)property.Body).Member as PropertyInfo;
if (propertyInfo == null)
{
throw new ArgumentException("The lambda expression 'property' should point to a valid Property");
}
else
{
var t = propertyInfo.GetType();
propertyInfo.SetValue(null, "");
}
}
我是这样称呼它的:
NoneLegend = "Bla bla...";
Utils.EmptyStringAfterXseconds(() => NoneLegend);
为了让视图中的绑定知道发生了什么变化,您需要通知它有关该变化的信息。 INotifyProperyChanged 接口就是为此目的而设计的。在下面的 ViewModel 代码中实现了这个接口。
我创建了一个简单的 WPF 程序。这是主窗口:
<Window x:Class="WpfApp1.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:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<StackPanel>
<Label x:Name="Legend" Content="{Binding Legend}"></Label>
</StackPanel>
</Grid>
</Window>
以及后面的代码:
using System.Windows;
namespace WpfApp1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel("Text should disappear");
}
}
}
和视图模型:
using System.ComponentModel;
using System.Threading;
namespace WpfApp1
{
public class ViewModel : INotifyPropertyChanged
{
Timer LegendTimer;
public ViewModel(string legend)
{
Legend = legend;
}
private void StartLegendTimer()
{
LegendTimer = new Timer(MakeDisappear, null, 3000, 3000);
}
private void StopLegendTimer()
{
LegendTimer.Dispose();
LegendTimer = null;
}
private void MakeDisappear(object state)
{
Legend = string.Empty;
StopLegendTimer();
}
private string _Legend;
/// <summary>
/// Here's where the magic happens
/// </summary>
public string Legend
{
get => _Legend;
set
{
// Each time the value is set ...
_Legend = value;
// we notify the view that a value has changed
NotifyPropertyChanged(nameof(Legend));
// If the value is not null or empty
if(!string.IsNullOrWhiteSpace(value))
{
// We start the time
StartLegendTimer();
}
}
}
private void NotifyPropertyChanged(string name)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
// The view automatically fills the PropertyChanged event
public event PropertyChangedEventHandler PropertyChanged;
}
}
这个怎么样。我有点担心 new ResetAfterTime()
电话。不知道实例是否存在足够长的时间。它可能会在计时器触发之前由垃圾收集器收集。不得不考虑一下,但它似乎工作正常。
通知程序class:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
namespace WpfApp1
{
public class PropertyNotifier : INotifyPropertyChanged
{
public void NotifyPropertyChanged(string name)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
// The view automatically fills the PropertyChanged event
public event PropertyChangedEventHandler PropertyChanged;
}
}
视图模型:
using System.ComponentModel;
using System.Threading;
namespace WpfApp1
{
public class ViewModel : PropertyNotifier
{
public ViewModel(string legend)
{
Legend = legend;
}
private string _Legend;
/// <summary>
/// Here's where the magic happens
/// </summary>
public string Legend
{
get => _Legend;
set
{
_Legend = value;
new ResetAfterTime<string>(this, () => Legend, value);
}
}
}
}
还有你的增强例程:
using System;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading;
namespace WpfApp1
{
public class ResetAfterTime<T>
where T : class
{
Timer _timer;
PropertyInfo _propertyInfo;
PropertyNotifier _notifier;
int _due;
public ResetAfterTime(PropertyNotifier notifier, Expression<Func<T>> property, T value, int due = 3000)
{
_notifier = notifier;
_propertyInfo = ((MemberExpression)property.Body).Member as PropertyInfo;
_due = due;
if (_propertyInfo == null)
{
throw new ArgumentException("The lambda expression 'property' should point to a valid Property");
}
else
{
if (value != default)
{
StartTimer();
}
}
}
private void StartTimer()
{
_timer = new Timer(MakeDisappear, null, _due, _due);
}
private void StopTimer()
{
_timer.Dispose();
_timer = null;
}
private void MakeDisappear(object state)
{
SetValue(null);
StopTimer();
}
private void SetValue(object value)
{
var t = _propertyInfo.GetType();
_propertyInfo.SetValue(_notifier, value);
_notifier.NotifyPropertyChanged(_propertyInfo.Name);
}
}
}
我正在开发一个用 WPF 编写的应用程序,代码是用 C# 编写的。 我有一个问号图标,当按下它时假设将内容设置为特定标签。 标签内容绑定到视图模型中的 属性,我们称之为 'NoneLegend'。 我希望 属性 在 5 秒后自行清除,因此我有一个实用程序 class 可以管理它。在 class 里面,我写了一个匿名方法来获取任何类型的 属性。 我的问题是如何将 属性 设置为 string.empty? 该方法如下所示:
public static void EmptyStringAfterXseconds<T>(Expression<Func<T>> property)
{
var propertyInfo = ((MemberExpression)property.Body).Member as PropertyInfo;
if (propertyInfo == null)
{
throw new ArgumentException("The lambda expression 'property' should point to a valid Property");
}
else
{
var t = propertyInfo.GetType();
propertyInfo.SetValue(null, "");
}
}
我是这样称呼它的:
NoneLegend = "Bla bla...";
Utils.EmptyStringAfterXseconds(() => NoneLegend);
为了让视图中的绑定知道发生了什么变化,您需要通知它有关该变化的信息。 INotifyProperyChanged 接口就是为此目的而设计的。在下面的 ViewModel 代码中实现了这个接口。
我创建了一个简单的 WPF 程序。这是主窗口:
<Window x:Class="WpfApp1.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:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<StackPanel>
<Label x:Name="Legend" Content="{Binding Legend}"></Label>
</StackPanel>
</Grid>
</Window>
以及后面的代码:
using System.Windows;
namespace WpfApp1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel("Text should disappear");
}
}
}
和视图模型:
using System.ComponentModel;
using System.Threading;
namespace WpfApp1
{
public class ViewModel : INotifyPropertyChanged
{
Timer LegendTimer;
public ViewModel(string legend)
{
Legend = legend;
}
private void StartLegendTimer()
{
LegendTimer = new Timer(MakeDisappear, null, 3000, 3000);
}
private void StopLegendTimer()
{
LegendTimer.Dispose();
LegendTimer = null;
}
private void MakeDisappear(object state)
{
Legend = string.Empty;
StopLegendTimer();
}
private string _Legend;
/// <summary>
/// Here's where the magic happens
/// </summary>
public string Legend
{
get => _Legend;
set
{
// Each time the value is set ...
_Legend = value;
// we notify the view that a value has changed
NotifyPropertyChanged(nameof(Legend));
// If the value is not null or empty
if(!string.IsNullOrWhiteSpace(value))
{
// We start the time
StartLegendTimer();
}
}
}
private void NotifyPropertyChanged(string name)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
// The view automatically fills the PropertyChanged event
public event PropertyChangedEventHandler PropertyChanged;
}
}
这个怎么样。我有点担心 new ResetAfterTime()
电话。不知道实例是否存在足够长的时间。它可能会在计时器触发之前由垃圾收集器收集。不得不考虑一下,但它似乎工作正常。
通知程序class:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
namespace WpfApp1
{
public class PropertyNotifier : INotifyPropertyChanged
{
public void NotifyPropertyChanged(string name)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
// The view automatically fills the PropertyChanged event
public event PropertyChangedEventHandler PropertyChanged;
}
}
视图模型:
using System.ComponentModel;
using System.Threading;
namespace WpfApp1
{
public class ViewModel : PropertyNotifier
{
public ViewModel(string legend)
{
Legend = legend;
}
private string _Legend;
/// <summary>
/// Here's where the magic happens
/// </summary>
public string Legend
{
get => _Legend;
set
{
_Legend = value;
new ResetAfterTime<string>(this, () => Legend, value);
}
}
}
}
还有你的增强例程:
using System;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading;
namespace WpfApp1
{
public class ResetAfterTime<T>
where T : class
{
Timer _timer;
PropertyInfo _propertyInfo;
PropertyNotifier _notifier;
int _due;
public ResetAfterTime(PropertyNotifier notifier, Expression<Func<T>> property, T value, int due = 3000)
{
_notifier = notifier;
_propertyInfo = ((MemberExpression)property.Body).Member as PropertyInfo;
_due = due;
if (_propertyInfo == null)
{
throw new ArgumentException("The lambda expression 'property' should point to a valid Property");
}
else
{
if (value != default)
{
StartTimer();
}
}
}
private void StartTimer()
{
_timer = new Timer(MakeDisappear, null, _due, _due);
}
private void StopTimer()
{
_timer.Dispose();
_timer = null;
}
private void MakeDisappear(object state)
{
SetValue(null);
StopTimer();
}
private void SetValue(object value)
{
var t = _propertyInfo.GetType();
_propertyInfo.SetValue(_notifier, value);
_notifier.NotifyPropertyChanged(_propertyInfo.Name);
}
}
}