wpf progressbar databind to command - wpf

I would like to use a progressbar in a simple way. I have a query that is run to return data to a grid when a user clicks a button. I would like to start the progressbar when the button is clicked and stop the progressbar when the data is returned to the grid.
I just want the progressbar to continue on (IsIndeterminate="True") to show that there is actually something happening.
Is there any way to bind the start and stop of the progressbar to properties or commands in my view model?
Thanks for any thoughts.

Use the IsIndeterminate property as your property to bind against a property on your ViewModel; mine is titled IsBusy in this example.
public partial class Window1 : Window
{
public MyViewModel _viewModel = new MyViewModel();
public Window1()
{
InitializeComponent();
this.DataContext = _viewModel;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
//this would be a command in your ViewModel, making life easy
_viewModel.IsBusy = !_viewModel.IsBusy;
}
}
public class MyViewModel : INotifyPropertyChanged
{
private bool _isBusy = false;
public bool IsBusy
{
get
{
return _isBusy;
}
set
{
_isBusy = value;
PropertyChangedEventHandler handler = PropertyChanged;
if(handler != null)
handler(this, new PropertyChangedEventArgs("IsBusy"));
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
The XAML is in this instance uses a click event handler for the Button; however in your instance you would simply bind your action which will start your processing to the command on your ViewModel.
<Grid>
<ProgressBar Width="100" Height="25" IsIndeterminate="{Binding IsBusy}"></ProgressBar>
<Button VerticalAlignment="Bottom" Click="Button_Click" Width="100" Height="25" Content="On/Off"/>
</Grid>
As you begin work and end work modifying the IsBusy property on your ViewModel will then start and stop the indeterminate behavior, providing the active/not-active visual appearance you are after.

You could expose a property that you then use to trigger the visibility of the ProgressBar, but you're better off using a control that encompasses a progress bar and exposes a property that turns it on/off. For example, the BusyIndicator in the Extended WPF Toolkit.

Related

DataContext of usercontrol in WPF

I'm new to WPF and I'm trying to start a little project with a maximum of good practice. I'm using MVVM and dependency injection.
I have a concern which seems to be easy to understand but i can't find an answer (at this step, DataContext is not very clear for me).
The UserControlView of type UserControl contains just a button for testing.
This is the app class :
public App()
{
IServiceCollection services = new ServiceCollection();
services.AddSingleton<MainWindow>();
services.AddSingleton<UserControlViewModel>();
services.AddSingleton<UserControlView>();
_serviceProvider = services.BuildServiceProvider();
}
The user control is included in the Main windows like that :
<Grid>
<views:UserControlView/>
</Grid>
Now, in the OnStartup overrided method :
protected override void OnStartup(StartupEventArgs e)
{
MainWindow = _serviceProvider.GetRequiredService<MainWindow>();
MainWindow.DataContext = _serviceProvider.GetRequiredService<PaymentMeansViewModel>();
MainWindow.Show();
}
Like that it works, my button is correctly binded to the command.
But what is strange for me is that I have to set the 'UserControlViewModel' as the DataContext of the Main Window.
Isn'it possible to bind it to the 'UserControlView', something like :
protected override void OnStartup(StartupEventArgs e)
{
MainWindow = _serviceProvider.GetRequiredService<MainWindow>();
UserControlView testUC = _serviceProvider.GetRequiredService<UserControlView>();
testUC.DataContext = _serviceProvider.GetRequiredService<UserControlViewModel>();
MainWindow.Show();
}
Thanks for help.
Finally I did it.
I think (I hope I'm right) that I understood.
First of all, let's begin with the basic.
A view must have a viewmodel to bind the properties. A usercontrol is a kind of view "encapsulated" in a view. Therefore a usercontrol must have its own viewmodel and the view must have its own viewmodel.
The datacontext of the MainWindow is set in the app onstartup method :
MainWindow = new MainWindow()
{
DataContext = new MainWindowViewModel()
};
MainWindow must implement INotifyPropertyChanged. All view models must implement this interface. We can create a base class which will be derived in the view models :
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
protected void OnPropertyChanged(string? propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
The DataContext of the usercontrol must be explicit in the xaml of the MainWindow:
<Grid>
<views:UserControlView DataContext="{Binding CurrentViewModel}"/>
</Grid>
"CurrentViewModel" is a DataContext, then it's a ViewModel, and as it is binded, it must be a property of the MainViewModel.
public class MainWindowViewModel : ViewModelBase
{
public ViewModelBase CurrentViewModel { get; }
public MainWindowViewModel()
{
CurrentViewModel=new UserControlViewModel();
}
}
Hope it can help.

How to find whether button single clicked or double clicked while executing custom commands for button

My Requirement: I want to set custom command for my WPF button, inside the custom command execution I want to know whether the command executed by mouse single click or double click. Also when the customCommand's CanExecute returns false, I want the button to go on disable state. please refer below for more details.
Description:
Hi, In WPF I have set custom command for my button. When I click the button(For both single click and double click) the command gets executed. Inside the custom command I want to handle a separate action for single click and double click. Is it possible to find whetehr button single clicked or double this inside commands?? I use .Net 4.0, c#4.0
Note : I referred this How to bind a command in WPF to a double click event handler of a control? but I faced a limitation here.
Limitation:
When I set the custom command for my button then on CustomCommand CanExcute returns false the button goes to disable state. but As per the above Suggestion, by setting the command to mouse binding and setting the mouse binding to button works but when CanExecute returns false, the button doesnt goes to disable state. How to overcome this
public CustomCommand: ICommandd
{
public bool CanExecute(object parameter)
{
//arbitrary logic
}
public void Execute(object parameter)
{
if(MouseSingleClick)
{
perform ActionA;
}
if(MouseDoubleClick)
{
PerformActionB;
}
}
}
Thanks in Advance.
I was able to use this and tweak it to use it in MVVM friendly way.
I am giving a working example using Cinch framework.
I hoe this helps you give the idea to get going.
MyViewModel
public class MyViewModel : INotifyPropertyChanged
{
private static DispatcherTimer myClickWaitTimer =
new DispatcherTimer (
new TimeSpan (0, 0, 0, 0, 150),
DispatcherPriority.Background,
mouseWaitTimer_Tick,
Dispatcher.CurrentDispatcher);
private static void mouseWaitTimer_Tick (object sender, EventArgs e)
{
myClickWaitTimer.Stop ();
Debug.WriteLine ("Single Click Executed");//PerformActionA
}
public ICommand CinchSingleClickCommand { get; private set; }
public ICommand CinchDoubleClickCommand { get; private set; }
public MyViewModel ()
{
CinchSingleClickCommand = new SimpleCommand<object, EventToCommandArgs> (CanExecuteSingleCinch, ExecuteSingleCinch);
CinchDoubleClickCommand = new SimpleCommand<object, EventToCommandArgs> (CanExecuteDoubleCinch, ExecuteDoubleCinch);
myClickWaitTimer.Stop ();
}
private void ExecuteDoubleCinch (EventToCommandArgs obj)
{
if (obj.EventArgs is MouseEventArgs)
{
myClickWaitTimer.Stop ();
Debug.WriteLine ("Double Click Executed");//PerformActionB
var mouseEvent = obj.EventArgs as MouseEventArgs;
mouseEvent.Handled = true;
}
}
private bool CanExecuteDoubleCinch (object arg)
{
return true;
}
private void ExecuteSingleCinch (EventToCommandArgs obj)
{
if (!(obj.EventArgs is MouseEventArgs))
{
myClickWaitTimer.Start ();
var mouseEvent = obj.EventArgs as RoutedEventArgs;
mouseEvent.Handled = true;
}
}
private bool CanExecuteSingleCinch (object arg)
{
return true;
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged (string propertyName)
{
var pc = PropertyChanged;
if (pc != null)
pc (this, new PropertyChangedEventArgs (propertyName));
}
}
You can play with the TimeSpan constructor to set how much delay do you want to keep between the single click and the double click.
The View
<Window x:Class="DataGridTesting.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:cinch="clr-namespace:Cinch;assembly=Cinch.WPF"
Title="MainWindow"
Height="350"
Width="525">
<DockPanel>
<Button x:Name="button"
Content="Test">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDoubleClick">
<cinch:EventToCommandTrigger Command="{Binding CinchDoubleClickCommand}" />
</i:EventTrigger>
<i:EventTrigger EventName="Click">
<cinch:EventToCommandTrigger Command="{Binding CinchSingleClickCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
</DockPanel>
</Window>
The Code behind for the view
public partial class MainWindow : Window
{
public MainWindow ()
{
InitializeComponent ();
this.DataContext = new MyViewModel ();
}
}
I used Nuget Package Manager to pull the required dll's for Cinch, System.Windows.Interactivity and Microsoft.Expression.Interactions
The confusion is Button control's default click event shadows over other events like double click, mouse down etc. So May be using a Label is a good idea. You can camouflage it as a button and then use MouseDown and MouseDoubleClick events of the label to do your two different tasks. Use a timer to differentiate single click. Following link shows more detail
double click/ single click

ICommand with MVVM in WPF

I am new to MVVM and WPF, trying to use ICommand in WPF and MVVM. Below is the code.
Can someone please help to know why the below code is not working, means nothing happens on button click.
Appreciate your help.
View
<Grid>
<Button Height="40" Width="200" Name="button1" Command="{Binding Path=Click}">Click Me</Button>
</Grid>
App.xaml.cs
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
MainWindow mainWindow = new MainWindow();
MainWindowViewModel vm = new MainWindowViewModel();
mainWindow.DataContext = vm;
}
}
MainWindowViewModel.cs
namespace TestWPFApplication.ViewModel
{
public class MainWindowViewModel
{
private ICommand _click;
public ICommand Click
{
get
{
if (_click == null)
{
_click = new CommandTest();
}
return _click;
}
set
{
_click = value;
}
}
private class CommandTest : ICommand
{
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
MessageBox.Show("Hi! Test");
}
}
}
}
It looks like your OnStartup method is instantiating a MainWindow and never showing it. You probably have the StartupUri set in XAML which is creating a different MainWindow with the data context not set.
You could remove the StartupUri and call mainWindow.Show(). Alternatively, you could get rid of the OnStartup method and set up the data context in the main window's constructor.
You don't need to initialize this Window in OnStartup.
In MainWindow constructor after Initialize create instance of ViewModel and it should work.

Alternative to groupbox?

I'm diving into WPF, coming from a Winforms background where I used groupboxes to show and hide "panels" depending on what menu options were clicked. This was a bit of a nightmare at designtime having multiple overlapping groupboxes.
Does WPF solve this problem? Is the groupbox still the way to go? Or are there better solutions?
The WPF GroupBox has a property on it called 'Visibility' (inherited from UIElement) which can be controlled in a View Model via binding.
This Xaml fragment shows a GroupBox's visibility being bound to a property called 'MyGroupBoxVisibility'...
<Grid>
<GroupBox Header="This and that" Visibility="{Binding MyGroupBoxVisibility}" Background="Plum"/>
</Grid>
A sample View Model which contains the 'MyGroupBoxVisibility' is...
public class ViewModel : INotifyPropertyChanged
{
public ViewModel()
{
HideTheGroupBox();
}
private void ShowTheGroupBox()
{
MyGroupBoxVisibility = Visibility.Visible;
}
private void HideTheGroupBox()
{
MyGroupBoxVisibility = Visibility.Collapsed;
}
private Visibility _myGroupBoxVisibility;
public Visibility MyGroupBoxVisibility
{
[DebuggerStepThrough]
get { return _myGroupBoxVisibility; }
[DebuggerStepThrough]
set
{
if (value != _myGroupBoxVisibility)
{
_myGroupBoxVisibility = value;
OnPropertyChanged("MyGroupBoxVisibility");
}
}
}
#region INotifyPropertyChanged Implementation
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string name)
{
var handler = System.Threading.Interlocked.CompareExchange(ref PropertyChanged, null, null);
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
#endregion
}
This sample has two methods in it which toggle the visibility back and forth. You can use this technique to transfer your WinForms stuff into WPF and also keep it within MVVM.
ETA: Note that it's vital to understand the difference between Collapsed and Hidden visibilities. Please see http://msdn.microsoft.com/en-us/library/system.windows.visibility(v=vs.110).aspx for an explanation

Silverlight MVVM binding updates fire in undesired order

Scenario: In a Silverlight 4 MVVM project, we have a ListBox control containing items, the selected item is two-way-bound to the appropriate property in the ViewModel. Another control (for example reasons, I've stripped it down to a single TextBox) is data bound to the selected item's content. The value should update on leave/focus lost.
Problem: When the value in the TextBox is changed and we leave that TextBox by pressing the Tab key, everything works as desired - the value is updated. However, if the user clicks on a different item in the ListBox, then the SelectedItem setter is fired before the content of TextBox setter is fired, leaving no chance to handle the user input.
You can see in debugger, when adding breakpoints to the property setters, that the new ListView selection is applied first, before the TextBox update is processed.
Desired behavior: We need to know that the currently selected item was modified before the user has selected another item. It's not desired to have a custom update trigger which would notify on each key press (we know that's possible).
Can you help?
Code (a very simple example):
ViewModel
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public class ItemViewModel : ViewModelBase
{
private string _content;
public ItemViewModel(string initContent)
{
_content = initContent;
}
public string Content
{
get
{
return _content;
}
set
{
if (_content != value)
{
_content = value;
OnPropertyChanged("Content");
}
}
}
}
public class MainViewModel : ViewModelBase
{
private ObservableCollection<ItemViewModel> _items =
new ObservableCollection<ItemViewModel>();
private ItemViewModel _selectedViewModel;
public ObservableCollection<ItemViewModel> Items
{
get
{
return _items;
}
}
public ItemViewModel SelectedItem
{
get
{
return _selectedViewModel;
}
set
{
if (_selectedViewModel != value)
{
_selectedViewModel = value;
OnPropertyChanged("SelectedItem");
}
}
}
}
XAML
<Grid x:Name="LayoutRoot" Background="White">
<ListBox Height="100"
HorizontalAlignment="Left"
Margin="12,12,0,0"
VerticalAlignment="Top"
ItemsSource="{Binding Items}"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
DisplayMemberPath="Content"
Width="220" />
<TextBox Height="23"
HorizontalAlignment="Left"
Margin="12,118,0,0"
Text="{Binding SelectedItem.Content, Mode=TwoWay}"
VerticalAlignment="Top"
Width="220" />
</Grid>
XAML Code Behind
public MvvmTestView()
{
InitializeComponent();
Loaded += new RoutedEventHandler(MvvmTestView_Loaded);
}
void MvvmTestView_Loaded(object sender, RoutedEventArgs e)
{
MainViewModel viewModel = new MainViewModel();
viewModel.Items.Add(new ItemViewModel("Hello StackOverflow"));
viewModel.Items.Add(new ItemViewModel("Thanks to Community"));
DataContext = viewModel;
}
UPDATE 1
I present a self designed solution for you to check out, which will be probably be the accepted one, I still want to encourage you to make comments and give your hints. Thanks.
You could add a behavior to your textbox to updated the binding every time the text is changed in the textbox. Maybe this solved your problems.
Here´s the code for the Behavior class:
public class UpdateTextBindingOnPropertyChanged : Behavior<TextBox> {
// Fields
private BindingExpression expression;
// Methods
protected override void OnAttached() {
base.OnAttached();
this.expression = base.AssociatedObject.GetBindingExpression(TextBox.TextProperty);
base.AssociatedObject.TextChanged+= OnTextChanged;
}
protected override void OnDetaching() {
base.OnDetaching();
base.AssociatedObject.TextChanged-= OnTextChanged;
this.expression = null;
}
private void OnTextChanged(object sender, EventArgs args) {
this.expression.UpdateSource();
}
}
Heres the XAML:
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:local="Namespace of the class where UpdateTextBindingOnPropertyChanged is defined"
<TextBox Text="{Binding SelectedItem.Content, Mode=TwoWay}">
<i:Interaction.Behaviors>
<local:UpdateTextBindingOnPropertyChanged />
</i:Interaction.Behaviors>
</TextBox >
This is one solution we currently came up with. It has the advantage that it separates different tasks to the appropriate layer. For example, the View enforces an update of the binding, while the ViewModel tells the View to do so. Another advantage is that its handled synchronously, which would for example allow to check the content right before switching away, and the call-stack remains unchanged without raising "External Code" (Going over Dispatcher or even DispatcherTimer would do so) which is better for maintenance and flow control. A disadvantage is the new Event which has to be bound and handled (and finally unbound. I present an anonymous handler only for example reasons).
How to get there?
In ViewModelBase, implement a new ForceBindingUpdate event:
public abstract class ViewModelBase : INotifyPropertyChanged
{
// ----- leave everything from original code ------
public event EventHandler ForceBindingUpdate;
protected void OnForceBindingUpdate()
{
var handler = ForceBindingUpdate;
if (handler != null)
handler(this, EventArgs.Empty);
}
}
In MainViewModel, update the setter of the SelectedItem property:
set // of SelectedItem Property
{
if (_selectedViewModel != value)
{
// Ensure Data Update - the new part
OnForceBindingUpdate();
// Old stuff
_selectedViewModel = value;
OnPropertyChanged("SelectedItem");
}
}
Update the MvvmTestView Code Behind to implement the new event:
void MvvmTestView_Loaded(object sender, RoutedEventArgs e)
{
// remains unchanged
Mvvm.MainViewModel viewModel = new Mvvm.MainViewModel();
viewModel.Items.Add(new Mvvm.ItemViewModel("Hello StackOverflow"));
viewModel.Items.Add(new Mvvm.ItemViewModel("Thanks to Community"));
// Ensure Data Update by rebinding the content property - the new part
viewModel.ForceBindingUpdate += (s, a) =>
{
var expr = ContentTextBox.GetBindingExpression(TextBox.TextProperty);
expr.UpdateSource();
};
// remains unchanged
DataContext = viewModel;
}
Last but not least, the minimal XAML Update: Give the TextBox a name by adding x:Name="ContentTextBox" Attribute to the TextBoxs XAML.
Done.
Actually, I don't know if this is the cleanest solution, but it gets close to what we had in mind.
Maybe you could handle TextBox LostFocus then (instead of listening to every key press)?
Other idea would be to keep a proxy property on the ViewModel instead of directly binding to SelectedItem.Content and writing some code to make sure the item is updated.
Solution №1
public class LazyTextBox: TextBox
{
//bind to that property instead..
public string LazyText
{
get { return (string)GetValue(LazyTextProperty); }
set { SetValue(LazyTextProperty, value); }
}
public static readonly DependencyProperty LazyTextProperty =
DependencyProperty.Register("LazyText", typeof(string), typeof(LazyTextBox),
new PropertyMetadata(null));
//call this method when it's really nessasary...
public void EnsureThatLazyTextEqualText()
{
if (this.Text != this.LazyText)
{
this.LazyText = this.Text;
}
}
}
Solution №2 (works as magic :) )
public class MainViewModel : ViewModelBase
{
private ObservableCollection<ItemViewModel> _items =
new ObservableCollection<ItemViewModel>();
private ItemViewModel _selectedViewModel;
public ObservableCollection<ItemViewModel> Items { get { return _items; } }
public ItemViewModel SelectedItem
{
get { return _selectedViewModel; }
set
{
if (_selectedViewModel != value)
{
if (SelectedItem != null)
{
SelectedItem.Content = SelectedItem.Content;
}
_selectedViewModel = value;
// A little delay make no harm :)
var t = new DispatcherTimer();
t.Interval = TimeSpan.FromSeconds(0.1);
t.Tick += new EventHandler(t_Tick);
t.Start();
}
}
}
void t_Tick(object sender, EventArgs e)
{
OnPropertyChanged("SelectedItem");
(sender as DispatcherTimer).Stop();
}
}
I know that in MVVM we do not want to put code in code behind. But in this instance it hurts nothing as it is entirely maintained in the UI and SOP is maintained.
By putting a ghost element to take focus we can swap the focus back in forth forcing
the text box to commit its contents. So in the code behind we take care of the focus wiggle.
But yet we still are using a relay command Update Command to execute the save. So the order is good as the Click event fires wiggling the view. And then the relay command UpdateCommand will fire and the textbox is committed and ready for update.
<MenuItem Header="_Save"
Command="{Binding UpdateCommand}" Click="MenuItem_Click">
</MenuItem>
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
UIElement elem = Keyboard.FocusedElement as UIElement;
Keyboard.Focus(ghost);
Keyboard.Focus(elem);
}
Solution #3
public abstract class ViewModelBase : INotifyPropertyChanged
{
private List<string> _propNameList = new List<string>();
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
_propNameList.Add(propertyName);
var t = new DispatcherTimer();
t.Interval = TimeSpan.FromSeconds(0);
t.Tick += new EventHandler(t_Tick);
t.Start();
}
void t_Tick(object sender, EventArgs e)
{
if (_propNameList.Count > 0)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(_propNameList[0]));
_propNameList.Remove(_propNameList[0]);
}
}
}
PS: it's the same timer.. but this solution is more generic..

Resources