简体   繁体   English

如何在不改变表单背后的代码的情况下每秒动态更新表单中的子控件?

[英]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.

相关问题 如何在C#中删除MDI表单中第二个(子)表单的控制框层作为父表单? - How do I remove the layer of control box of the second(child) form within the MDI form as parent form in C#? 如何从另一个子表单更新一个子表单 - How do I update a child form from another child form 如何关闭子表单和子表单的子表单? - How do I close a child form of a child form AND the child form? 如何在不激活C#中包含计时器的表单的情况下执行计时器控件的代码? - How do I execute the code for a timer control without activating the form that contains the timer in C#? 如何在不刷新页面的情况下显示每秒变化的值? - How do I display a value that changing every second without doing a page refresh? 如何在不将该颜色应用于每个子控件的情况下更改 GroupBox 的 ForeColor? - How do I change the ForeColor of a GroupBox without having that color applied to every child control as well? 如何从子窗体更新MDI父窗体中的控件? - How to update a control in MDI parent form from child forms? 如何在c#中创建一个类,以便我可以单击1表单上的按钮来更新第二个表单上的数据? - How do I make a class in c# so I can click a button on 1 form to update data on a second form? 如何通过用户控件将文本从子级传递到主窗体? - How do I pass text from child to main form via a user control? 如何在WebBrowser控件中提交表单? - how do I submit a form in a WebBrowser control?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM