[英]How do I dynamicaly update a child control within a form every second without changing the code behind the form?
I have a child control within a form which shows the strength of the WiFi signal As this application is to be used on a tablet whithin a moving vehicle round the site. 我在一个表格中有一个儿童控制器,显示了WiFi信号的强度。因为这个应用程序将在平板电脑上使用在车辆周围的移动车辆上。 I would like to have this control within every form of my WPF application and I would like it to refresh every second without having to change the code behind each form it is on.
我想在我的WPF应用程序的每个形式中都有这个控件,我希望它每秒刷新一次,而不必更改它所在的每个表单后面的代码。
It is possible to refresh this control using the parent form's code behind by calling signalQualityView.Refresh(); 通过调用signalQualityView.Refresh(),可以使用父窗体的代码来刷新此控件。 every second but would like this functionality to be implemented within the SignalQualityView usercontrol .
每秒钟,但希望在SignalQualityView用户控件中实现此功能。
public partial class SignalQualityView : UserControl, INotifyPropertyChanged
{
// wifi signal indicator taken from from https://stackoverflow.com/questions/20085284/c-sharp-wpf-rating-control-similar-to-wifi-signal-indicator
private NetworkInformationService _networkInformationService;
public SignalQualityView()
{
InitializeComponent();
DataContext = this;
_networkInformationService = new NetworkInformationService();
Refresh();
}
public void Refresh()
{
Task.Run(() =>
{
WirelessNetwork wirelessNetwork = _networkInformationService.GetWirelessNetworkDetails();
var signalQuality = wirelessNetwork.SignalQuality;
if (signalQuality >= 80)
RatingValue = 5;
else if (signalQuality >= 60)
RatingValue = 4;
else if (signalQuality >= 40)
RatingValue = 3;
else if (signalQuality >= 20)
RatingValue = 2;
else if (signalQuality >= 1)
RatingValue = 1;
else
RatingValue = 0;
Task.Delay(1000);
Refresh();
});
}
public int RatingValue
{
get { return (int)GetValue(RatingValueProperty); }
set
{
SetValue(RatingValueProperty, value);
OnPropertyChanged();
}
}
// Using a DependencyProperty as the backing store for RatingValue. This enables animation, styling, binding, etc...
public static readonly DependencyProperty RatingValueProperty =
DependencyProperty.Register("RatingValue", typeof(int), typeof(SignalQualityView), new UIPropertyMetadata(0));
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class RatingConverter : IValueConverter
{
public Brush OnBrush { get; set; }
public Brush OffBrush { get; set; }
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
int rating = 0;
int number = 0;
if (int.TryParse(value.ToString(), out rating) && int.TryParse(parameter.ToString(), out number))
{
if (rating >= number)
{
return OnBrush;
}
return OffBrush;
}
return Brushes.Transparent;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
<UserControl x:Class="App.PlugIn.Controls.SignalQualityView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:DPWorld.PlugIn.Controls"
mc:Ignorable="d"
Height="40" Width="60">
<Grid Background="black" >
<Grid.Resources>
<local:RatingConverter x:Key="RatingConverter" OnBrush="LightBlue" OffBrush="Black" />
<Style TargetType="Rectangle">
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="VerticalAlignment" Value="Bottom" />
<Setter Property="Margin" Value="5,0,0,0" />
</Style>
</Grid.Resources>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Background="Black" VerticalAlignment="Center">
<Rectangle Width="5" Height="5" Fill="{Binding RatingValue, Converter={StaticResource RatingConverter}, ConverterParameter=1}"/>
<Rectangle Width="5" Height="10" Fill="{Binding RatingValue, Converter={StaticResource RatingConverter}, ConverterParameter=2}"/>
<Rectangle Width="5" Height="15" Fill="{Binding RatingValue, Converter={StaticResource RatingConverter}, ConverterParameter=3}"/>
<Rectangle Width="5" Height="20" Fill="{Binding RatingValue, Converter={StaticResource RatingConverter}, ConverterParameter=4}"/>
<Rectangle Width="5" Height="25" Fill="{Binding RatingValue, Converter={StaticResource RatingConverter}, ConverterParameter=5}"/>
</StackPanel>
<Label Content="SIGNAL" Foreground="LightBlue" VerticalAlignment="Top" Height="15" FontSize="8" Margin="10,0,18,0" Padding="0,0,0,0"/>
</Grid>
</UserControl>
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:pi="clr-namespace:App.PlugIn.Controls;assembly=PlugIn"
Title="Window1" Height="300" Width="300">
<Grid>
<pi:SignalQualityView Name="signalQualityView" />
</Grid>
</Window>
The number of signal bars are meant highlighted depending on the signal strength but 0 bars are highlighted. 根据信号强度突出显示信号条的数量,但突出显示0条。
When using WPF, data on a window that changes should come from an IObservable property of the bound object. 使用WPF时,更改窗口上的数据应来自绑定对象的IObservable属性。 When the property changes, the display value changes.
属性更改时,显示值会更改。 Put the current value into a property of a shared object using a task that runs every second.
使用每秒运行一次的任务将当前值放入共享对象的属性中。 Share the object across all bound objects and no code behind is required.
在所有绑定对象之间共享对象,不需要后面的代码。
You start of with a class that can be shared and observed: 您可以从一个可以共享和观察的类开始:
public class WiFiStrength : IObservable<int>
{
private int _signalQuality;
private int _ratingValue;
private class Unsubscriber : IDisposable
{
private List<IObserver<int>> _observers;
private readonly IObserver<int> _observer;
public Unsubscriber(List<IObserver<int>> observers, IObserver<int> observer)
{
_observers = observers;
_observer = observer;
}
public void Dispose()
{
if (_observers != null)
{
_observers.Remove(_observer);
_observers = null;
}
}
}
private List<IObserver<int>> _observers = new List<IObserver<int>>();
private void SetAndRaiseIfChanged(int newRating)
{
if (_ratingValue != newRating)
{
_ratingValue = newRating;
foreach (var observer in _observers)
{
observer.OnNext(newRating);
}
}
}
private static WiFiStrength _sharedInstance;
private static Task _updater;
private static CancellationTokenSource cts;
private static void UpdateStrength(WiFiStrength model, CancellationToken ct)
{
var rnd = new Random();
while (!ct.IsCancellationRequested)
{
model.SignalQuality = rnd.Next(100);
Thread.Sleep(1000);
}
}
public static WiFiStrength SharedInstance()
{
if (_sharedInstance == null)
{
_sharedInstance = new WiFiStrength();
cts = new CancellationTokenSource();
_updater = new Task(() => UpdateStrength(_sharedInstance, cts.Token));
_updater.Start();
}
return _sharedInstance;
}
public int SignalQuality
{
get => _signalQuality;
set
{
_signalQuality = value;
if (_signalQuality >= 80)
{
SetAndRaiseIfChanged(5);
}
else if (_signalQuality >= 60)
{
SetAndRaiseIfChanged(4);
}
else if (_signalQuality >= 40)
{
SetAndRaiseIfChanged(3);
}
else if (_signalQuality >= 20)
{
SetAndRaiseIfChanged(2);
}
else if (_signalQuality >= 1)
{
SetAndRaiseIfChanged(1);
}
else
{
SetAndRaiseIfChanged(0);
}
}
}
public int SignalRating => _ratingValue;
public IDisposable Subscribe(IObserver<int> observer)
{
_observers.Add(observer);
return new Unsubscriber(_observers, observer);
}
}
You attach it to your bound class: 您将它附加到您的绑定类:
public class ViewModel1 : INotifyPropertyChanged
{
public string TextField { get; set; }
public WiFiStrength WiFi { get; set; }
private IObserver<int> _observer;
private class WiFiObserver : IObserver<int>
{
private readonly ViewModel1 _parent;
private readonly IDisposable _unsubscribe;
public WiFiObserver(ViewModel1 parent, WiFiStrength observed)
{
_parent = parent;
_unsubscribe = parent.WiFi.Subscribe(this);
}
public void OnNext(int value)
{
_parent.NotifyPropertyChanged("WiFi");
}
public void OnError(Exception error)
{
_unsubscribe.Dispose();
}
public void OnCompleted()
{
_unsubscribe.Dispose();
}
}
public ViewModel1()
{
WiFi = WiFiStrength.SharedInstance();
_observer = new WiFiObserver(this, WiFi);
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Your class looks for changes, and notifies the owning form if it does change. 您的班级会查找更改,并在更改时通知拥有的表单。
Your XAML need only bind to the property like this: 您的XAML只需绑定到属性,如下所示:
<Label Content="{Binding WiFi.SignalRating, Mode=OneWay}"/>
Use on as many forms as you like. 使用尽可能多的表单。
A simple UserControl that updates itself periodically would use a DispatcherTimer
with a Tick
event handler that updates a dependency property of the control. 定期更新的简单UserControl将使用带有
Tick
事件处理程序的DispatcherTimer
来更新控件的依赖项属性。 An element in the control's XAML would bind the control property by a RelativeSource
Binding. 控件的XAML中的元素将通过
RelativeSource
绑定绑定控件属性。
A simple digital clock as example: 以简单的数字时钟为例:
<UserControl ...>
<Grid>
<TextBlock Text="{Binding Time,
RelativeSource={RelativeSource AncestorType=UserControl}}"/>
</Grid>
</UserControl>
Code behind: 代码背后:
public partial class Clock : UserControl
{
public Clock()
{
InitializeComponent();
var timer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(0.5) };
timer.Tick += (s, e) => Time = DateTime.Now.ToString("HH:mm:ss");
timer.Start();
}
public static readonly DependencyProperty TimeProperty =
DependencyProperty.Register(nameof(Time), typeof(string), typeof(Clock));
public string Time
{
get { return (string)GetValue(TimeProperty); }
set { SetValue(TimeProperty, value); }
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.