Displaying random numbers with a DispatcherTimer - c#

I've been looking for an answer but none seem to fit my question.
I am trying to adapt the MvVM method, but I dont think I fully understand it..
I'm trying to create an RPM display in WPF.
I want it to display an number (between 0-3000) and update this number every second (into a TextBlock).
I have created a new class where I try to create a DispatcherTimer and Random generator and then put that in the UI TextBlock.
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Threading;
namespace Aldeba.UI.WpfClient
{
public class GenerateRpm
{
public GenerateRpm()
{
DispatcherTimer timer = new DispatcherTimer
{
Interval = new TimeSpan(0, 0, 5)
};
timer.Start();
timer.Tick += Timer_Tick;
}
public int RandomValue()
{
Random random = new Random();
int RandomRpm = random.Next(0, 3001);
return RandomRpm;
}
void Timer_Tick(object sender, EventArgs e)
{
MainWindow mainWindow = new MainWindow();
GenerateRpm rpm = new GenerateRpm();
mainWindow.RpmDisplayLabel.Text = rpm.RandomValue().ToString();
}
}
}
My MainWindow.xaml.cs looks like...
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainWindowViewModel();
this.DataContext = new GenerateRpm();
}
}
}
Do I need to add datacontext to all classes I want to access (for bindings for an example)?
This is the MainWindow where I want the Rpm displayed in the second TextBlock.
<StackPanel Orientation="Horizontal" Grid.Row="0" Grid.Column="1" HorizontalAlignment="Right" VerticalAlignment="Center">
<TextBlock Text="RPM:" Style="{StaticResource RpmDisplay}" />
<TextBlock x:Name="RpmDisplayLabel" Text="{Binding }" Style="{StaticResource RpmDisplay}" />
</StackPanel>
What am I missing and/ or doing wrong to be able to do this?

Use a view model like shown below, with a public property that is cyclically updated by the timer.
Make sure the property setter fires a change notification, e.g. the PropertyChanged event of the INotifyPropertyChanged interface.
public class MainWindowViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private readonly Random random = new Random();
public MainWindowViewModel()
{
var timer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(1) };
timer.Tick += Timer_Tick;
timer.Start();
}
private int randomRpm;
public int RandomRpm
{
get { return randomRpm; }
set
{
randomRpm = value;
PropertyChanged?.Invoke(
this, new PropertyChangedEventArgs(nameof(RandomRpm)));
}
}
private void Timer_Tick(object sender, EventArgs e)
{
RandomRpm = random.Next(0, 3001);
}
}
Assign an instance of the view model class to the MainWindow's DataContext:
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel();
}
In the view, bind an element to the view model property:
<TextBlock Text="{Binding RandomRpm}"/>

Related

How to send TextBox value from modal window to main window programmatically?

I'm trying to learn WPF MVVM I would need to understand how to update a textbox value via a modal window. Below the code, I wrote passes the value to the viewmodel but does not update the textbox. Thanks in advance
UserControl con il TextBox
<TextBox x:Name="Text01UC" Text="{Binding TextUC, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" Width="200" Height="33"/>
UserControl Behind
namespace InvioDati
{
public partial class textbox : UserControl
{
public textbox()
{
InitializeComponent();
var vm = new ModelTextView();
this.DataContext = vm;
vm.Load();
}
private void Open_Click(object sender, RoutedEventArgs e)
{
MoadalWindow md = new MoadalWindow();
md.ShowDialog();
}
}
}
ModelTextView
namespace InvioDati
{
class ModelTextView : BaseViewModel
{
private ModelText dati = new ModelText();
public string TextUC
{
get => dati.TextVal;
set
{
dati.TextVal = value;
OnPropertyChanged();
}
}
public void Load() {
TextUC = "GoodMorning";
}
public void Ricevi(string valore)
{
TextUC = valore;
}
}
}
ModalWindow Code behind
namespace InvioDati
{
public partial class MoadalWindow : Window
{
public MoadalWindow()
{
InitializeComponent();
}
private void Test_Click(object sender, RoutedEventArgs e)
{
ModelTextView nd = new ModelTextView();
nd.Ricevi(Send.Text);
this.Close();
}
}
}
Set the DataContext of the ModalWindow to the same instance of ModelTextView in textbox.xaml.cs:
private void Open_Click(object sender, RoutedEventArgs e)
{
MoadalWindow md = new MoadalWindow();
md.DataContext = this.DataContext;
md.ShowDialog();
}
You can then either bind directly to the TextUC property or do the following in ModalWindow.xaml.cs:
private void Test_Click(object sender, RoutedEventArgs e)
{
ModelTextView nd = DataContext as ModelTextView;
nd.Ricevi(Send.Text);
this.Close();
}
You must use a mediator in order not to break mvvm here.
Check https://en.wikipedia.org/wiki/Mediator_pattern#C#
1b. Add Observer pattern to create notifications for value changes.
Dialogs are evil within MVVM, usually you won't need them. What you want is an overlaying View, which can be Data bound in any way as there is no break in the visual tree
If you want to use "dialogs", implement a DialogService to do so.
Edit: here is a draft on how you create something "popup" like in the most simple way:
<UserControl>
<Grid>
<!--Invert visability of all controls below via binding-->
<YourMainControl/>
<Rect Fill="Black" Opacity=".5 Visibility="Hidden"/>
<YourSubControl Visibility="Hidden"/>
</Grid>
</UserControl>

Updating control property breaks OneWay binding?

Consider this very simple example where I have a UserControl like this:
UserControl XAML:
<UserControl x:Class="BindingTest.SomeControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="SomeControlElement">
<Grid>
<TextBlock Text="{Binding ElementName=SomeControlElement, Path=Counter}" />
</Grid>
</UserControl>
Code Behind:
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;
namespace BindingTest
{
public partial class SomeControl : UserControl
{
public SomeControl()
{
InitializeComponent();
var timer = new DispatcherTimer();
timer.Interval = new TimeSpan(0, 0, 5);
timer.Tick += (s, e) => Counter = Counter + 1;
timer.Start();
}
public int Counter
{
get { return (int)GetValue(CounterProperty); }
set { SetValue(CounterProperty, value); }
}
public static readonly DependencyProperty CounterProperty = DependencyProperty.Register(nameof(Counter), typeof(int), typeof(SomeControl), new PropertyMetadata(0));
}
}
So the Control just shows a TextBlock and every 5 seconds increments the Counter. And then I have a consumer of course:
MainWindow XAML:
<Window x:Class="BindingTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:BindingTest"
x:Name="MainWindowName" Width="200" Height="300">
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<StackPanel>
<local:SomeControl Counter="{Binding ElementName=MainWindowName, Path=SomeSource, Mode=OneWay}" />
<local:SomeControl Counter="{Binding ElementName=MainWindowName, Path=SomeSource, Mode=TwoWay}" />
</StackPanel>
</Grid>
</Window>
And lastly the Main Code behind:
using System;
using System.Windows;
using System.ComponentModel;
using System.Windows.Threading;
namespace BindingTest
{
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
var timer = new DispatcherTimer();
timer.Interval = new TimeSpan(0, 0, 1);
timer.Tick += (s, e) => SomeSource = SomeSource + 1;
timer.Start();
}
private int someSource;
public int SomeSource
{
get => someSource;
set
{
if (someSource != value)
{
someSource = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SomeSource)));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
Right, so the main has a counter in code behind that updates a property every second. The XAML has 2 instances of the UserControl. One that has OneWay binding, and one that has TwoWay binding.
What I see here, is that when the Counter in "SomeControl.cs" updates, the binding for the first UserControl (OneWay) is broken. The one with TwoWay keeps on updating.
Is this by design (and why)? And more importantly, if I have the need for updating properties in my UserControls, how would I do this in my example - in order to support OneWay bindings? Mind that, I'm really not interested in TwoWay binding in this example because it would update "MySource" which is not what I wanted!
Thanks.
It's by design. When you assign a so-called local value to a dependency property, a previously assigned OneWay Binding is replaced. A TwoWay Binding remains active and updates its source property.
There is however a workaround. Do not set a local value, but a "current value". Replace
timer.Tick += (s, e) => Counter = Counter + 1;
with
timer.Tick += (s, e) => SetCurrentValue(CounterProperty, Counter + 1);

System.Timers.Timer Elapsed excute command not work with RelayCommand

I am creating a custom user control which use a timer to count the time and run the command action in the view model finally.
Problem
When the time passed, it run the elapsed event, then execute a static command.
The fact is that when I click the refresh button, it can enter the RefreshCommand_Executed (it is expected). However, it cannot enter this function for the timer elasped event fired even then code in BeginInvoke is run (it is unexpected)...
Please help for this.
Code
-CustomControl.xaml.cs
public partial class CustomControl : UserControl
{
public static ICommand ExecuteCommand = new RoutedCommand();
public CustomControl()
{
System.Timers.Timer timer = new System.Timers.Timer();
timer.AutoReset = true;
timer.Interval = 60000.0;
timer.Elapsed += (sender, e) =>
{
this.Dispatcher.BeginInvoke(new Action(() =>
{
if (ExecuteCommand != null)
{
ExecuteCommand.Execute(sender);
}
}));
};
timer.Start();
}
private void ExecuteCommand_Executed(object sender, RoutedEventArgs e)
{
if (ExecuteCommand != null)
{
ExecuteCommand.Execute(sender);
}
}
}
-CustomControl.xaml
<UserControl ...skip...>
<Grid>
<Button x:Name="refreshButton"
Content="Refresh"
Click="ExecuteCommand_Executed" />
</Grid>
</UserControl>
-MainView.xaml
<UserControl ...skip...>
<UserControl.Resources>
<vm:MainViewModel x:Key="ViewModel" />
</UserControl.Resources>
<Grid cmd:RelayCommandBinding.ViewModel="{StaticResource ViewModel}">
<cmd:RelayCommandBinding Command="ctr:CustomControl.ExecuteCommand" CommandName="RefreshCommand" />
</Grid>
</UserControl>
-MainViewModel.cs
public class MainViewModel : NotifyPropertyChanged
{
private ICommand refreshCommand;
public ICommand RefreshCommand
{
get { return refreshCommand; }
set { if (value != refreshCommand) { refreshCommand = value; RaisePropertyChanged("RefreshCommand"); } }
}
public MainViewModel()
{
RefreshCommand = new RelayCommand(RefreshCommand_Executed);
}
void RefreshCommand_Executed(object o)
{
//code to run
}
}
Your timer is probably garbage collected. Try to keep a reference of it in your control and check if it works.
By the way, you can use a Dispatcher Timer and avoid using the dispatcher yourself.

Navigation in C# and XAML

I want to navigate to another page in my Windows Phone 8.1 app.I can easily do this if there is a button by clicking on it and using Frame.Navigate(typeof(MainPage)); in the event handler.But in my case I want to navigate to a second page automatically depending upon an integer value.If it becomes zero,the page automatically goes to second page.In my case I dont have a button and so event handler so to do this.How can I do this?
Implement the INotifyPropertyChanged interface for your view model. Here is a crude implementation, ideally, you would use an mvvm framework and send messages to your views as required.
View Model
public class GameStateViewModel : INotifyPropertyChanged
{
private int currentScore = 10;
/// <summary>
/// The timer here added to simulate whatever is supposed to be changing your value.
/// </summary>
public GameStateViewModel()
{
var timer = new DispatcherTimer
{
Interval = TimeSpan.FromSeconds(2)
};
timer.Tick += (sender, args) =>
{
if (this.CurrentScore > 0)
{
this.CurrentScore--;
}
else
{
timer.Stop();
}
};
timer.Start();
}
public event PropertyChangedEventHandler PropertyChanged;
public int CurrentScore
{
get { return currentScore; }
set
{
currentScore = value;
NotifyPropertyChanged("CurrentScore");
}
}
public void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Code behind
public partial class MainPage : PhoneApplicationPage
{
// Constructor
public MainPage()
{
InitializeComponent();
var viewModel = new GameStateViewModel();
viewModel.PropertyChanged += (sender, args) =>
{
if (viewModel.CurrentScore <= 0)
{
this.NavigationService.Navigate(new Uri("/Page2.xaml", UriKind.Relative));
}
};
this.DataContext = viewModel;
}
}
Xaml
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<TextBlock Text="{Binding CurrentScore}" FontSize="{StaticResource PhoneFontSizeExtraLarge}" />
</Grid>

WPF, Property does not return value to the binding

So, I have a project with a scrolling text (marqee) that rotates over a string array. And I want it to change the string value after 20 seconds of each animation iteration.
There is a problem though, the property(ScrollingText) that uses the INotifyPropertyChanged interface to bind to a textblock(using XAML) does not return after the first iteration. Even though it refreshes normally(in the set part), it does not return on the Getter part.... except for the first set in the default ctor.
MAIN CLASS:
class GetScrollingText : CommonBase
{
private string _scrollingtext = String.Empty;
DoubleAnimation Animation;
public GetScrollingText()
{
ScrollingText = GetScrollString();
}
public string ScrollingText
{
get
{
return _scrollingtext;
}
set
{
if (value != _scrollingtext)
{
_scrollingtext = value;
RaisePropertyChanged("ScrollingText");
}
}
} // INJECTS the string in the animated textblock {binding}.
public TextBlock scrollBlock { get; set; }
string GetScrollString()
{
.........
return scrolltext;
}
public void LeftToRightMarqee(double from, double to)
{
Animation = new DoubleAnimation();
Animation.From = from;
Animation.To = to;
Animation.Duration = new Duration(TimeSpan.FromSeconds(20));
Animation.Completed += animation_Completed;
scrollBlock.BeginAnimation(Canvas.LeftProperty, Animation);
}
void animation_Completed(object sender, EventArgs e)
{
ScrollingText = GetScrollString();
scrollBlock.BeginAnimation(Canvas.LeftProperty, Animation);
}
}
For some reason the animation_Completed Event only changes the value ScrollingText, but it does not invoke the Getter part therefore there is not a return to the {binding}.
XAML:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:AnnouncingSys"
x:Class="AnnouncingSys.MainWindow"
x:Name="Window"
Width="1280" Height="720" MinHeight="566" MinWidth="710">
<Window.Resources>
<vm:GetScrollingText x:Key="ScrollingText"/>
</Window.Resources>
<Canvas x:Name="MainCanvas" ClipToBounds="True" Margin="0,0,0,0" Grid.Row="5" Background="Black" Grid.ColumnSpan="5" >
<TextBlock x:Name="ScrollBlock" TextWrapping="Wrap" VerticalAlignment="Top" Height="113" Width="5147" Canvas.Left="-1922" Text="{Binding ScrollingText, Source={StaticResource ScrollingText}}"/>
</Canvas>
</Window>
CODE BEHIND:
public partial class MainWindow : Window
{
GetScrollingText scrolling = new GetScrollingText();
public MainWindow()
{
InitializeComponent();
scrolling.scrollBlock = this.ScrollBlock;
scrolling.LeftToRightMarqee(2000, -3000);
}
}
And finally the helper class CommonBase:
public class CommonBase : INotifyPropertyChanged
{
protected CommonBase()
{
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string PropertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
PropertyChangedEventArgs e = new PropertyChangedEventArgs(PropertyName);
handler(this, e);
}
}
}
I have even put a breakpoint on the return block of the Getter but it only activates on the first: "ScrollingText = GetScrollString()". I mean, shouldn't it return each time the value is changed???
You are using two different instances of your GetScrollingText class, one in XAML as StaticResource, the other in code behind as the scrolling field in class MainWindow.
Instead of creating a StaticResource in XAML, you could just set the DataContext property of your MainWindow:
public partial class MainWindow : Window
{
GetScrollingText scrolling = new GetScrollingText();
public MainWindow()
{
InitializeComponent();
scrolling.scrollBlock = this.ScrollBlock;
scrolling.LeftToRightMarqee(2000, -3000);
DataContext = scrolling; // here
}
}
Now you would not explicitly set the binding's Source property, because the DataContext is used as default binding source:
<TextBlock ... Text="{Binding ScrollingText}"/>

Categories