Как динамически обновлять дочерний элемент управления в форме каждую секунду, не меняя код позади формы? - PullRequest
0 голосов
/ 02 апреля 2019

У меня есть дочерний элемент управления в форме, которая показывает силу сигнала 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 полос подсвечено.

Ответы [ 2 ]

0 голосов
/ 02 апреля 2019

Простой UserControl, который периодически обновляется, будет использовать DispatcherTimer с обработчиком событий Tick, который обновляет свойство зависимости элемента управления. Элемент в XAML элемента управления связывает свойство элемента управления с помощью RelativeSource Binding.

Простые цифровые часы в качестве примера:

<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); }
    }
}
0 голосов
/ 02 апреля 2019

При использовании 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}"/>

Используйте столько форм, сколько хотите.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...