I have something like this in xaml:
<Button Content="{Binding MyStopwatch.IsRunning,
Converter={StaticResource BoolToStr}}"/>
I need to display Start, when IsRunning is false and Stop, when IsRunning is true.
I have no problem with converter or binding itself.
I have problem with refreshing IsRunning property.
When IsRunning property change while programm is running - it does not change Start/Stop text.
I know how to implement INotifyPropertyChange on my own properties.
But I dont know how to implement (something like) property change on IsRunning
If you want to update your bindings, you can call the PropertyChanged on property MyStopwatch whenever you start or stop a stopwatch.
OnPropertyChanged("MyStopwatch");
You can't make StopWatch implement INotifyPropertyChanged. What you can do, is create your own wrapper for it, and use that instead. For example:
public class StopwatchWrapper : INotifyPropertyChanged
{
Stopwatch _stopwatch;
private bool _isRunning;
public bool IsRunning
{
get { return _isRunning; }
set
{
if (_isRunning != value)
{
_isRunning = value;
OnPropertyChanged("IsRunning");
}
}
}
public StopwatchWrapper()
{
_stopwatch = new Stopwatch();
_isRunning = false;
}
public void Start()
{
_stopwatch.Start();
IsRunning = _stopwatch.IsRunning;
}
public void Stop()
{
_stopwatch.Stop();
IsRunning = _stopwatch.IsRunning;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Related
I'm binding an activity indicator to a property called IsLoading to show that the page is busy (e.g. processing API calls). I need to implement this in all my MAUI app pages, so my question, how can I re-use this code? The property is implemented as follows:
public event PropertyChangedEventHandler PropertyChanged;
private bool isLoading;
public bool IsLoading
{
get => isLoading;
set
{
isLoading = value;
OnPropertyChanged();
}
}
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
You probably want to put this in a base class that you inherit from. For example.
Create a class/file BaseViewModel.cs, of course the name can be whatever you want. It might look like this:
public class BaseViewModel
{
public event PropertyChangedEventHandler PropertyChanged;
private bool isLoading;
public bool IsLoading
{
get => isLoading;
set
{
isLoading = value;
OnPropertyChanged();
}
}
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Now, whenever you create a new view model, you need to inherit from this. Let's say that you create a EditPersonViewModel, that would look like this:
public class EditPersonViewModel : BaseViewModel // This is where you inherit from the BaseViewModel
{
private bool isSaved;
public bool IsSaved
{
get => isSaved;
set
{
isSaved = value;
OnPropertyChanged();
}
}
}
Now you have access to all the things that are also in BaseViewModel. So you can set IsLoading to true or false, or you can implement a new property, IsSaved in my above example, and you can still call OnPropertyChanged to make the UI aware of the value change.
I have two different objects instantiated in my UWP program. Their class definitions look like this:
namespace StackOverflow
{
public class FirstClass : INotifiyPropertyChanged
{
private bool isEnabled = false;
public bool IsEnabled
{
get => isEnabled;
set
{
isEnabled = value;
NotifyPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public class SecondClass : INotifiyPropertyChanged
{
private bool isEnabled = false;
public bool IsEnabled
{
get => isEnabled;
set
{
isEnabled = value;
NotifyPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
The binding on the XAML side currently looks like this:
<Button Content="Press me if you can" IsEnabled="{x:Bind FirstClass.IsEnabled, Mode=OneWay}"/>
With this code, Button.IsEnabled is only dependent on FirstClass.IsEnabled, what I'd like to achieve is for the Button.IsEnabled property to be dependent on FirstClass.IsEnabled AND SecondClass.IsEnabled. How can I obtain such a result?
I wanted to implement MultiBinding, but it seems to be a WPF-only feature
We can't bind two properties directly in xaml.It seems to only be able to operate in code-behind.When you set the IsEnabled of FirstClass or SecondClass,then judge the IsEnabled of Button manually.
I have a small problem async loader in a view with WPF MVVM pattern .
I wish that when I click the button Read from UserControl View , send a Command to view model , and then it was off a loading before making execute and it disappeared when I return data .
The data is read by a machine via optical USB .
All works , the program runs perfectly, just can not bring up the loading so async . The loading is displayed along with the return of reading , because synchronous . How can I do asynchronous ? I tried it with the task but it seems he does not consider the code .
class ReadAndPrintFromDevice : ICommand
{
async Task<int> showLoader()
{
model.ShowLoader = true;
return 1;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public async void Execute(object parameter)
{
showLoader async here
//other code..
showloader async shadow here, after other code
}
}
if you need more information tell in the comments that I add all .
I would suggest something like this:
public class ReadAndPrintFromDeviceAsyncCommand : PropertyChangedBase, ICommand
{
private bool _IsBusy;
public bool IsBusy
{
get { return _IsBusy; }
private set
{
_IsBusy = value;
NotifyOfPropertyChange();
}
}
public bool CanExecute(object parameter)
{
return !IsBusy;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public async void Execute(object parameter)
{
IsBusy = true;
await ...
IsBusy = false;
}
}
By using an IsBusy property, you can simply set that to be true before executing the asynchronous operation, and once it's done, set it back to false.
You can then bind your control to the IsBusy property:
<MyControl Visibility="{Binding SomeCommand.IsBusy, ...}"
The above example would require a BooleanToVisibilityConverter to work, but I think you get the idea.
Also, the command uses a PropertyChangedBase which I have created to simply make implementing INotifyPropertyChanged a bit easier, the code for this class is here:
public abstract class PropertyChangedBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyOfPropertyChange([CallerMemberName]string propertyName = null)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
I have an MVVM app, where my ViewModel PingerViewModel processes incoming WCF Ping() messages. Processing such a message happens on a thread of Scheduler.Default's thread pool.
Semantically, incoming WCF messages change a bound property CanPing and raise the PropertyChanged event for said property.
But my UI is not updating until it receives some UI event, e.g. focusing/clicking the window, etc.
How do I make it update as soon as the event is fired?
I have tried raising the PropertyChanged event...
on the Application's Dispatcher,
using a SynchronizationContext
without any luck.
I also verified that the bound property is indeed set to the proper value, and that there is indeed a listener consuming my PropertyChanged event.
Here's some code (full code on github):
part of my view's MainWindow.xaml:
It might be worth noting that the bound Command does not actually play a role in producing the incoming WCF message.
<Button Content="Ping" Height="23" HorizontalAlignment="Left" Margin="10,10,0,0" Name="PingBtn" VerticalAlignment="Top" Width="75" AutomationProperties.AutomationId="Ping"
IsEnabled="{Binding CanPing}"
Command="{Binding PingCommand}" />
part of my views MainWindow.xaml.cs
public MainWindow()
{
DataContext = new PingerViewModel();
InitializeComponent();
}
part of my ViewModel
public class PingerViewModel : INotifyPropertyChanged
public PingerViewModel()
{
Pinger = new Pinger(true);
PingCommand = new PingerPingCommand(this);
//...
}
public bool CanPing
{
get
{
if (Pinger == null) return false;
return Pinger.CanPing;
}
}
public void Ping()
{
_pingClient.Channel.Ping();
Pinger.CanPing = false;
OnPropertyChanged("CanPing");
}
protected virtual void OnPong(PongEventArgs e)
{
Pinger.CanPing = true;
OnPropertyChanged("CanPing");
}
public Pinger Pinger { get; private set; }
public ICommand PingCommand { get; private set; }
//...
}
I think you need to remove IsEnabled="{Binding CanPing}" from your button.
Binding to the command is enough as the ICommand object contains CanExecute and the CanExecuteChanged event handler.
I would create a CanExecute boolean inside your Command class, and implement INotifyPropertyChanged also on this class. Something like this:
public class PingCommand : ICommand, INotifyPropertyChanged
{
private bool _canExecute;
public bool CanExecute1
{
get { return _canExecute; }
set
{
if (value.Equals(_canExecute)) return;
_canExecute = value;
CanExecuteChanged.Invoke(null, null);
OnPropertyChanged("CanExecute1");
}
}
public void Execute(object parameter)
{
//whatever
}
public bool CanExecute(object parameter)
{
return _canExecute;
}
public event EventHandler CanExecuteChanged;
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Then, on the Ping/Pong methods inside your ViewModel, update this property inside your Command:
public void Ping()
{
_pingClient.Channel.Ping();
Pinger.CanPing = false;
PingCommand.CanExecute1 = false;
OnPropertyChanged("CanPing");
}
protected virtual void OnPong(PongEventArgs e)
{
Pinger.CanPing = true;
PingCommand.CanExecute1 = true;
OnPropertyChanged("CanPing");
}
if your CanPing Property and the CanExecute methode for your PingCommand both return TRUE it should work.
sometimes the Delegate/RelayCommand implementation give the possibility to call RaiseCanExecuteChanged() - try this if the statement above is true for both and its not working
btw this is called within the RaiseCanExecuteChanged()
CommandManagerHelper.CallWeakReferenceHandlers(_canExecuteChangedHandlers);
You can use RaiseCanExecuteChanged() method of the said property to update.
Eg:
this.PingCommand.RaiseCanExecuteChanged();
Try this, I hope it will solve your problem.
I apologize for the newbie question, but I am struggling with this problem. I have the following TextBlock defined:
<TextBlock Text="{Binding Source={x:Static local:DeviceManager.Instance},
Path=Player.CurrentArtist}"></TextBlock>
The DeviceManager is a singleton that functions as a facade for other classes. For example, Player is a property of type IPlayer which represents an music-playing application. I would like the TextBlock to display the artist that is currently playing, which is periodically updated in the Player.CurrentArtist property.
Unfortunately, I cannot get the TextBlock to update when the CurrentArtist property updates. Both the DeviceManager and the IPlayer implement INotifyPropertyChanged, but when I step through the application, the DeviceManager does not have an event handler attached to it.
Does anyone have a suggestion for how to update the text block while preserving the singleton-facade?
Here is the code for the INotifyPropertyChanged members in both the DeviceManager and the IPlayer subclass:
public sealed class DeviceManager : INotifyPropertyChanged
{
// Singleton members omitted
public IPlayer Player
{
get { return player; }
set
{
this.player = value;
player.PropertyChanged += new PropertyChangedEventHandler(device_PropertyChanged);
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void device_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(sender, e);
}
}
#endregion
}
class MediaPlayer : IPlayer
{
private string artist;
private string title;
public event PropertyChangedEventHandler PropertyChanged;
public void Play(string artist, string title)
{
this.artist = artist;
this.title = title;
OnPropertyChanged("Player:Song");
}
private void OnPropertyChanged(string p)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(p));
}
}
public string CurrentTitle
{
get { return title; }
}
public string CurrentArtist
{
get { return artist; }
}
}
The problem is that WPF is never notified of the value of the CurrentArtist property changing. You can either implement a private setter for the CurrentArtist property, which will trigger the PropertyChanged event, or trigger a PropertyChanged event for the CurrentArtist property in MediaPlayer.Play().
WPF only responds to PropertyChanged if the name you pass in (i.e. right now "Player:Song") is the same as the property you're bound to - change the PropertyChanged to "CurrentArtist" and you'll see it update properly.
You are not raising the PropertyChanged event, what you need is:
public sealed class DeviceManager : INotifyPropertyChanged
{
// Singleton members omitted
public IPlayer Player
{
get { return player; }
set
{
this.player = value;
OnPropertyChanged(this, new PropertyChangedEventArgs("Player"));
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(sender, e);
}
}
#endregion
}
How does the UI know when you change the Player property? From that code it does not look like it raises PropertyChanged to me. Can you post a complete working sample of the problem? Otherwise we're forced to just guess.