簡體   English   中英

如何在不改變表單背后的代碼的情況下每秒動態更新表單中的子控件?

[英]How do I dynamicaly update a child control within a form every second without changing the code behind the form?

我在一個表格中有一個兒童控制器,顯示了WiFi信號的強度。因為這個應用程序將在平板電腦上使用在車​​輛周圍的移動車輛上。 我想在我的WPF應用程序的每個形式中都有這個控件,我希望它每秒刷新一次,而不必更改它所在的每個表單后面的代碼。

通過調用signalQualityView.Refresh(),可以使用父窗體的代碼來刷新此控件。 每秒鍾,但希望在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>

根據信號強度突出顯示信號條的數量,但突出顯示0條。

使用WPF時,更改窗口上的數據應來自綁定對象的IObservable屬性。 屬性更改時,顯示值會更改。 使用每秒運行一次的任務將當前值放入共享對象的屬性中。 在所有綁定對象之間共享對象,不需要后面的代碼。

您可以從一個可以共享和觀察的類開始:

   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);
      }
   }

您將它附加到您的綁定類:

   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));
      }
   }

您的班級會查找更改,並在更改時通知擁有的表單。

您的XAML只需綁定到屬性,如下所示:

         <Label Content="{Binding WiFi.SignalRating, Mode=OneWay}"/>

使用盡可能多的表單。

定期更新的簡單UserControl將使用帶有Tick事件處理程序的DispatcherTimer來更新控件的依賴項屬性。 控件的XAML中的元素將通過RelativeSource綁定綁定控件屬性。

以簡單的數字時鍾為例:

<UserControl ...>
    <Grid>
        <TextBlock Text="{Binding Time,
            RelativeSource={RelativeSource AncestorType=UserControl}}"/>
    </Grid>
</UserControl>

代碼背后:

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM