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.
Related
So I'm currently looking into commands and what I've done as of right now is that I made a command, bound it to a button and I've passed in a bool property which will indicate whether or not the button can execute the command.
The issue is that the button starts off as enabled, and as soon as I click it, it turns disabled, and then the person gets added to the ListView, but the button stays disabled.
I'm not sure if I need to add a UpdateSourceTrigger to the command, I thought the whole point of implementing a ObservableObject was to not have to do that.
What's the proper way of achieving what I'm trying to achieve?
The Command
public class RelayCommand : ICommand
{
private Action<object> execute;
private Func<object, bool> canExecute;
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
{
this.execute = execute;
this.canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return this.canExecute == null || this.canExecute(parameter);
}
public void Execute(object parameter)
{
this.execute(parameter);
}
}
The MainWindow (DataContext)
<Window x:Class="Commands.MainWindow"
...
Title="MainWindow"
Height="450"
Width="800">
<Window.DataContext>
<viewmodel:MainViewModel />
</Window.DataContext>
<StackPanel HorizontalAlignment="Center"
VerticalAlignment="Center">
<ListView MinHeight="100"
ItemsSource="{Binding PeopleCollection}">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button Content="Add"
Width="100"
Height="25"
Command="{Binding AddPersonCommand}" />
</StackPanel>
</Window>
The MainViewModel
internal class MainViewModel : ObservableObject
{
public ObservableCollection<PersonModel> PeopleCollection { get; set; }
/* A slow working command */
public RelayCommand AddPersonCommand { get; set; }
private bool _canExecute;
public bool CanExecute
{
get { return _canExecute; }
set
{
_canExecute = value;
NotifyPropertyChanged("CanExecute");
}
}
public MainViewModel()
{
CanExecute = true;
PeopleCollection = new ObservableCollection<PersonModel>();
AddPersonCommand = new RelayCommand(o => AddPersonToCollection(o), (p) => CanExecute);
}
private async void AddPersonToCollection(object o)
{
CanExecute = false;
await Task.Factory.StartNew(() =>
{
/* Simulate a heavy workload*/
Thread.Sleep(5000);
Application.Current.Dispatcher.BeginInvoke(() =>
{
PeopleCollection.Add(new PersonModel
{
Id = 0,
Name = "Foo bar"
});
});
/* Property needs to be updated from the main thread? */
//CanExecute = false;
});
CanExecute = false;
}
}
The ObservableObject
internal class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
public void NotifyPropertyChanged([CallerMemberName] String propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
I'm failing to see how your Button is actually being marked as disabled. I see the CanExecute bool being manipulated, but your button doesn't have IsEnabled bound. Even if that is the case, as #PaulSinnema said, you are only setting CanExecute to false (besides the first set in the constructor). I would suggest trying to set CanExecute = true inside of your Task and inside your lambda function (in the BeginInvoke) so then you can ensure the timing is correct. As you currently have it, your AddPersonToCollection method is doing the following:
Sets CanExecute to false
Fires off a task and waits for it to complete
Sets CanExecute to false
Here's my suggestion, hopefully it helps:
private async void AddPersonToCollection(object o)
{
CanExecute = false;
await Task.Factory.StartNew(() =>
{
/* Simulate a heavy workload*/
Thread.Sleep(5000);
Application.Current.Dispatcher.BeginInvoke(() =>
{
PeopleCollection.Add(new PersonModel
{
Id = 0,
Name = "Foo bar"
});
// Setting it here ensures we flip it as soon as the collection has a person added
CanExecute = true;
});
});
// Remove this line
///CanExecute = false;
}
I am using an external SDK.
namespace ProSimSDK
{
public class ArmedFailure
{
...
public static event ArmedFailureEventDelegate onNew;
public void Reset();
...
}
}
namespace ProSimSDK
{
public delegate void ArmedFailureEventDelegate(ArmedFailure armedFailure);
}
I have some trouble when I try to rewrite some Winform code by WPF. In Winform:
public Form1()
{
InitializeComponent();
ArmedFailure.onNew += new ArmedFailureEventDelegate(ArmedFailure_onNew);
}
// This function will be called when a new armedFailure is received
void ArmedFailure_onNew(ArmedFailure armedFailure)
{
//Here is the code I need to rewrite in WPF.
removeButton.Click += new EventHandler(delegate(object sender, EventArgs e)
{
failure.Reset();
});
}
In WPF, I use a listbox. With some guide, I am using ListBox Template and Command.
In Window1.xaml:
<DataTemplate x:Key="ListBoxItemTemplate">
<Grid>
<TextBlock x:Name="TB" Margin="5,10,5,5" Grid.Column="2" Height="23" Text="{Binding}" VerticalAlignment="Top" />
<Button HorizontalAlignment="Right" Grid.Column="3" Margin="500,10,5,0" CommandParameter="{Binding}" Command="{Binding ElementName=UC_Failures_Setting, Path=OnClickCommand}" Width="80" Click="Button_Click">remove</Button>
</Grid>
</DataTemplate>
<ListBox x:Name="listbox" ItemTemplate="{StaticResource ListBoxItemTemplate}" Margin="0,661,982,0" SelectionChanged="ListBox_SelectionChanged">
Window1.xaml.cs
public Window1()
{
InitializeComponent();
//How to implement the same functionality of "removeButton.Click += new EventHandler(delegate(object sender, EventArgs e) {failure.Reset();});" shown in Winform???
OnClickCommand = new ActionCommand(x => listbox.Items.Remove(x));
}
ActionCommand.cs:
public class ActionCommand: ICommand
{
private readonly Action<object> Action;
private readonly Predicate<object> Predicate;
public ActionCommand(Action<object> action) : this(action, x => true)
{
}
public ActionCommand(Action<object> action, Predicate<object> predicate)
{
Action = action;
Predicate = predicate;
}
public bool CanExecute(object parameter)
{
return Predicate(parameter);
}
public void Execute(object parameter)
{
Action(parameter);
}
public event EventHandler CanExecuteChanged
{
add
{
CommandManager.RequerySuggested += value;
}
remove
{
CommandManager.RequerySuggested -= value;
}
}
}
How does the button in my listbox implement the same functionality of
removeButton.Click += new EventHandler(delegate(object sender, EventArgs e)
{ failure.Reset(); });
shown in Winform? In WPF I cannot write it this way. Thanks.
if ListBox is populated with ArmedFailure items, then the parameter which command receives, should be ArmedFailure item.
OnClickCommand = new ActionCommand
(
x =>
{
var failure = (ArmedFailure)x;
failure.Reset();
listbox.Items.Remove(x);
}
);
everything that was in Button.Click handler in WinForms becomes part of ICommand.Execute in wpf
I have a listbox in a wpf window thats bound to a list in a viewmodel object. When I run a method in the viewmodel object it processes members of the list and each member has a progress. I would like to have the gui update continuously during execution. As it is now, it only updates gui when the processing is finished.
Here I have tried to create a small example of what I have right now:
MainWindow.xaml:
<Window x:Class="WPF_MVVM_Thread_Progressbar.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WPF_MVVM_Thread_Progressbar"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:TestViewModel/>
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="8*"/>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="8*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ListBox Grid.Column="0" Grid.Row="0" Margin="5" ItemsSource="{Binding TestWorker.TestList}">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<ProgressBar Minimum="0" Maximum="100" Value="{Binding Progress, Mode=OneWay}" Background="Bisque">
<ProgressBar.Style>
<Style TargetType="{x:Type ProgressBar}">
<Style.Triggers>
<DataTrigger Binding="{Binding Progress}" Value="0">
<Setter Property="Visibility" Value="Hidden"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ProgressBar.Style>
</ProgressBar>
<TextBlock FontWeight="Bold" Text="{Binding Path=Name}" Background="Transparent"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Grid.Column="0" Grid.Row="1" Content="TestRun" Command="{Binding TestRunCommand}"></Button>
<TextBlock Text="{Binding SelectedIdx}" Grid.Column="1" Grid.Row="1"/>
</Grid>
</Window>
MainWindowl.xaml.cs:
using Prism.Commands;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
namespace WPF_MVVM_Thread_Progressbar
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
public class TestViewModel : INotifyPropertyChanged
{
private WorkingClass _testWorker;
private DelegateCommand _testRunCommand;
public DelegateCommand TestRunCommand
{
get { return _testRunCommand; }
set { _testRunCommand = value; }
}
public WorkingClass TestWorker
{
get { return _testWorker; }
set { _testWorker = value; RaisePropertyChanged("TestWork"); }
}
private int _selectedIdx;
public int SelectedIdx
{
get { return _selectedIdx; }
set { _selectedIdx = value; RaisePropertyChanged("SelectedIdx"); }
}
public TestViewModel()
{
_testWorker = new WorkingClass();
_testRunCommand = new DelegateCommand(TestRun, canRun);
}
public async void TestRun()
{
//await Task.Run(() => _testWorker.Work());
_testWorker.Work();
}
private bool canRun()
{
return true;
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class WorkingClass : INotifyPropertyChanged
{
private ObservableCollection<TestObject> _testList;
public ObservableCollection<TestObject> TestList
{
get { return _testList; }
set { _testList = value; RaisePropertyChanged("TestList"); }
}
public WorkingClass()
{
_testList = new ObservableCollection<TestObject>();
_testList.Add(new TestObject("Object A"));
_testList.Add(new TestObject("Object B"));
_testList.Add(new TestObject("Object C"));
RaisePropertyChanged("TestList");
}
public void Work()
{
foreach (var obj in TestList)
{
obj.TestWork();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class TestObject : INotifyPropertyChanged
{
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
private int _progress;
public int Progress
{
get { return _progress; }
set { _progress = value; RaisePropertyChanged("Progress"); }
}
public TestObject(string name)
{
this._name = name;
_progress = 0;
}
public void TestWork()
{
for (int i = 0; i < 100; i++)
{
System.Threading.Thread.Sleep(10);
Progress++;
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
I have tried to use ObservableCollection and INotifyPropertyChanged but this it seems not to be enough.
Eventually I would like to be able to have the same effect using async/await call from the TestViewModel.TestRun().
Could someone perhaps offer some insights on this? It would be much appreciated.
I think the current reason that you have the UI only updating once completed, is that you are running all of this on the UI thread. I would instead try this:
Task.Run(async delegate
{
await _testWorker.Work();
});
Or
Task.Run(() =>
{
_testWorker.Work();
});
Or
Task.Factory.StartNew(() =>
{
_testWorker.Work();
});
Or
var newThread = new Thread(new ThreadStart(_testWorker.Work());
newThread.Start();
This will return back to the UI instantly but allow your code to continue.
Note: You will have to be careful about the use of objects off the UI thread. ObservableCollections can only be created on the same thread as the dispatcher that handles the UI work. If you are using two-way binding, again you have to be careful about thread safety.
I've successfully done this in the past using a BackgroundWorker.
public class TestObject : INotifyPropertyChanged {
private BackgroundWorker worker;
public TestObject() {
worker = new BackgroundWorker() {
WorkerReportsProgress = true
};
worker.DoWork += DoWork;
worker.ProgressChanged += WorkProgress;
worker.RunWorkerCompleted += WorkFinished;
}
public int Progress
{
get { return _progress; }
set { _progress = value; RaisePropertyChanged("Progress"); }
}
// Begin doing work
public void TestWork() {
worker.RunWorkerAsync();
}
private void DoWork(object sender, DoWorkEventArgs eventArgs) {
worker.ReportProgress(0, "Work started");
for (int i = 0; i < 100; i++) {
System.Threading.Thread.Sleep(10);
worker.ReportProgress(i, "Message");
}
}
// Fires when the progress of a job changes.
private void WorkProgress(object sender, ProgressChangedEventArgs e) {
// Do something with the progress here
Progress = e.ProgressPercentage;
}
// Fires when a job finishes.
private void WorkFinished(object sender, RunWorkerCompletedEventArgs e) {
// The work finished. Do something?
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
// NOTE: If you're running C#6 use the null conditional operator for this check.
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(e));
}
}
A BackgroundWorker basically runs everything on a separate thread and reports back when its progress changes or it finishes working. You can pull out the ProgressPercentage from its progress report and use that in the UI. Hope that helps. To keep the example simple I didn't include some of your code but that should given you an idea of how it can be done.
The same questions has been asked many times on this site and I have read most of them. But I have a special problem (maybe?) that couldn't figure it out after hours of struggling and reading SO posts.
The problem is -simply explained, I have a WPF form which contains a Connect button. If this button is pressed a textblock must appear on that form, displaying the word "Connecting...". Upon pressing the button, some handshaking operations are done in the associated C# code which takes some time. If the program fails to connect, the textblock must change to "Failed!". Otherwise, it changes to "Succeed."
Now for this simple problem, I wrote in my XAML:
<Window x:Class="WpfTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="300" Width="200">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Button x:Name="connecting" Content="Connect" FontWeight="Bold" Click="startConnection"
Width="60" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Row="0"/>
<TextBlock x:Name="comm_stat" Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center"
Text="{Binding Content}"/>
</Grid>
</Window>
And the C# code (inspired by this answer):
using System;
using System.Text;
using System.Windows;
using System.ComponentModel;
namespace WpfTest
{
public class DynamicObj : INotifyPropertyChanged
{
public DynamicObj() : this(string.Empty) { }
public DynamicObj(string txt) { Content = txt; }
private string _name;
public string Content
{
get { return _name; }
set {
_name = value;
OnPropertyChanged("Content");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string PropertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
}
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
comm_stat.DataContext = new DynamicObj();
}
private void startConnection(object sender, RoutedEventArgs e)
{
comm_stat.Text = "Connecting...";
bool connect2device = false;
// do the handshaking operations. the result is then assigned to connect2device
comm_stat.Text = connect2device ? "Succeed." : "Failed!";
// some other operations
}
}
}
Now the problem is, whenever I click the button, no text is appeared in the textblock. Because the program waits for the startConnection method to reach its end and then updates the bonded textblock. But I want the textblock to change right after pressing the button. How can I do this?
You can use BackgroundWorker as such:
bool connect2device = false;
private void startConnection(object sender, RoutedEventArgs e)
{
comm_stat.Text = "Connecting...";
// do the handshaking operations. the result is then assigned to connect2device
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += DoWork;
worker.RunWorkerCompleted += Completed;
worker.RunWorkerAsync();
}
private void Completed(object sender, RunWorkerCompletedEventArgs e)
{
comm_stat.Text = connect2device ? "Succeed." : "Failed!";
}
private void DoWork(object sender, DoWorkEventArgs e)
{
//Change with actual work.
Thread.Sleep(1000);
connect2device = true;
}
One side note is that you actually do not use bindings to change the text. comm_stat.Text = "Connecting..."; sets the text property directly and the DynamicObj object is not used at all. It might be good for you to read a few tutorial on MVVM.
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>