I have SettingsViewModel with:
public class SettingsViewModel : BaseViewModel, ISettingsViewModel
{
public SettingsViewModel()
{
}
private string _gaugeColor;
public string GaugeColor
{
get => Preferences.Get("GaugeColor", "#17805d");
set
{
Preferences.Set("GaugeColor", value);
this.OnSettingsChanged();
this.OnPropertyChanged();
}
}
public event EventHandler<SettingsChangedEventArgs> SettingsChanged;
private void OnSettingsChanged() => this.SettingsChanged?.Invoke(this, new SettingsChangedEventArgs(this.Settings));
public Settings Settings { get; private set; }
}
}
I set color by HEX string.
Then in PanelViewModel I have:
private Color _gaugeColor;
public Color GaugeColor
{
get => Color.FromHex(Preferences.Get("GaugeColor", "#17805d"));
set
{
_gaugeColor = value;
OnPropertyChanged();
}
}
Now if I change HEX string from Settings view in UI, color does not change in PanelViewModel until I restart an application.
Question is: How to make color change in PanelViewModel right after it has been changed in SettingsViewModel?
I have tried to add this into PanelViewModel, but apparently this creates a new instance of SettingsViewModel and Color does not follow into PanelViewMode. Maybe there is some direct solution and I am using Xamarin.Essentials wrong?
public PanelViewModel()
{
this.SettingsViewModel = new SettingsViewModel();
this.SettingsViewModel.SettingsChanged += OnSettingsChanged;
}
private Color _gaugeColor;
public Color GaugeColor
{
get => Color.FromHex(Preferences.Get("GaugeColor", "#17805d"));
set
{
_gaugeColor = value;
OnPropertyChanged();
}
}
private void OnSettingsChanged(object sender, SettingsChangedEventArgs e)
{
this.GaugeColor = Color.FromHex(e.Settings.GaugeColor);
}
private SettingsViewModel SettingsViewModel { get; }
https://learn.microsoft.com/en-us/dotnet/api/xamarin.essentials.preferences?view=xamarin-essentials
Question is: How to make color change in PanelViewModel right after it
has been changed in SettingsViewModel?
Yes, a simple method is to use MessagingCenter.
The MessagingCenter class implements the publish-subscribe pattern, allowing message-based communication between components that are inconvenient to link by object and type references. This mechanism allows publishers and subscribers to communicate without having a reference to each other, helping to reduce dependencies between them.
You can refer to the following code:
In SettingsViewModel.cs, we can publish a message in the constuctor of it,just as follows:
public class SettingsViewModel
{
public string Title { get; set; }
public SettingsViewModel() {
Title = "SettingsView";
MessagingCenter.Send<Object, Color>(this, "Hi", Color.Yellow);
}
}
And in PanelViewModel.cs ,we can subscribe to this message:
public class PanelViewModel
{
public string Title { get; set; }
public PanelViewModel() {
Title = "PanelView";
MessagingCenter.Subscribe<Object, Color>(this, "Hi", async (sender, arg) =>
{
System.Diagnostics.Debug.WriteLine("-----> receive color= " + arg);
// here ,we can use the received color to update the UI
});
}
}
Note:
Once we receive the color, we can use the received color to update the UI, and the color field in the PanelViewModel.cs should implement interface INotifyPropertyChanged.
Related
So, I have a statusbar as UserControl.
Model:
public class StatusBarModel : BindableBase
{
private string _status;
public string Status
{
get { return _status; }
set
{
_status = value;
RaisePropertyChanged("Status");
}
}
private int _p_value;
public int P_Value
{
get { return _p_value; }
set
{
_p_value = value;
RaisePropertyChanged("P_Value");
}
}
}
ViewModel:
public class StatusBarVM : BindableBase
{
readonly source.elements.StatusBar.StatusBarModel _model = new source.elements.StatusBar.StatusBarModel();
public StatusBarVM()
{
_model.PropertyChanged += (s, e) => { RaisePropertyChanged(e.PropertyName); };
}
public string Status
{
get { return _model.Status; }
set { _model.Status = value; }
}
public int P_Value
{
get { return _model.P_Value; }
set { _model.P_Value = value; }
}
}
And for example I wanna change Status variable from others ViewModels.
How I can do it? I have seen examples with only buttons and etc.
There are multiple ways to achieve your requirement. as #bitclicker says, you can use static class that hold its value. But I think It is too much that makes it static class, because that variable value may be used only two viewmodel.
I suggest you communicate between two view model. you will find Prism's event aggregator or you could implement your own event publish-subscriber model. making your own event pub-sub model would help you to make a first step into the design pattern.
You could create a static class to hold that value.
public static class Globals()
{
public static StatusBarModel GlobalStatus { get; set; }
}
Then whenever you want to alter it you just do
Globals.GlobalStatus.Status = "something";
Globals.GlobalStatus.P_Value = 14;
does that accomplish what you need?
I would like to have a control that allows a property to be shown if another property's value is set to a specific value. The following is a much simplified example of what I would like:
public class CustomButton : Control
{
private ButtonType _bType = ButtonType.OnOff;
private Int32 _minPress = 50; // 50 mS
public ButtonType Button_Type
{
get { return _bType; }
set { _bType = value; }
}
public Int32 Minimum_Press_Time // Only for momentary buttons
{
get { return _minPress; }
set { _minPress = value; }
}
}
public enum ButtonType
{
Momentary,
OnOff
}
On adding CustomButton to a Windows.Forms form, the Minimum_Press_Time will only show in the Properties window if Button_Type is changed to ButtonType.Momentary.
Is such a thing possible?
Yes, its possible to get close but it looks a little strange. I've done this on some controls before. Here is a full example of what you would need to do:
public partial class CustomButton : Control
{
private ButtonType _buttonType = ButtonType.OnOff;
private CustomButtonOptions _options = new OnOffButtonOptions();
[RefreshProperties(System.ComponentModel.RefreshProperties.All)]
public ButtonType ButtonType
{
get { return _buttonType; }
set
{
switch (value)
{
case DynamicPropertiesTest.ButtonType.Momentary:
_options = new MomentaryButtonOptions();
break;
default:
_options = new OnOffButtonOptions();
break;
}
_buttonType = value;
}
}
[TypeConverter(typeof(ExpandableObjectConverter))]
public CustomButtonOptions ButtonOptions
{
get { return _options; }
set { _options = value; }
}
public CustomButton()
{
InitializeComponent();
}
}
public enum ButtonType
{
Momentary,
OnOff
}
public abstract class CustomButtonOptions
{
}
public class MomentaryButtonOptions : CustomButtonOptions
{
public int Minimum_Press_Time { get; set; }
public override string ToString()
{
return Minimum_Press_Time.ToString();
}
}
public class OnOffButtonOptions : CustomButtonOptions
{
public override string ToString()
{
return "No Options";
}
}
So basically what is happening is you are using an ExpandableObjectConverter to convert an abstract type to a set of options. You then use the RefreshProperties attribute to tell the property grid that it will need to refresh the properties after this property changes.
This is the easiest way I've found to come as close to what you are asking for as possible. The property grid doesn't always refresh the right way so sometimes there will be a "+" sign next to an options set with no expandable properties. Use the "ToString" in the properties to make the display on the property grid look intelligent.
I'm writing an android app that uses the MediaPlayer. I've created a custom IAudioPlayer as a wrapper for the MediaPlayer so that I can eventually extend it to iOS.
public interface IAudioPlayer
{
bool IsPlaying { get; }
void Play(string fileName, int startingPoint);
void Play(string fileName);
void Pause();
void Stop();
int CurrentPosition();
bool HasFile();
void SkipForward(int seconds);
void SkipBackward(int seconds);
void SeekTo(int seconds);
}
Our app is structured using the Mvvm pattern and is using MvvmCross.
On our FragmentViewModel we've got commands such as IPlayCommand, IStopCommand, etc.
public class HomeFragmentViewModel : ViewModelBase
{
public HomeFragmentViewModel(IPlayCommand playCommand,
IStopCommand stopCommand,
ISkipForwardCommand skipForwardCommand,
ISkipBackwardCommand skipBackwardCommand)
{
_playCommand = playCommand;
_stopCommand = stopCommand;
_skipForwardCommand = skipForwardCommand;
_skipBackwardCommand = skipBackwardCommand;
}
private string _playPauseIcon = FontAwesome.icon_play;
public string PlayPauseIcon
{
get { return _playPauseIcon; }
set { _playPauseIcon = value; RaisePropertyChanged(() => PlayPauseIcon); }
}
private IPlayCommand _playCommand;
public IPlayCommand PlayCommand
{
get { return _playCommand; }
set { _playCommand = value; RaisePropertyChanged(() => PlayCommand); }
}
private IStopCommand _stopCommand;
public IStopCommand StopCommand
{
get { return _stopCommand; }
set { _stopCommand = value; RaisePropertyChanged(() => StopCommand); }
}
private ISkipForwardCommand _skipForwardCommand;
public ISkipForwardCommand SkipForwardCommand
{
get { return _skipForwardCommand; }
set { _skipForwardCommand = value; RaisePropertyChanged(() => SkipForwardCommand); }
}
private ISkipBackwardCommand _skipBackwardCommand;
public ISkipBackwardCommand SkipBackwardCommand
{
get { return _skipBackwardCommand; }
set { _skipBackwardCommand = value; RaisePropertyChanged(() => SkipBackwardCommand); }
}
}
On our View we've got buttons that bind to those commands, and all is working as expected.
However
Our view also has a SeekBar that we're going to use for quick scrubbing by the user. The seekbar needs to do two things...
allow the user quickly navigate to the spot that's required (this one "should" be easy
automatically update based on the track's current progress (this one I'm stuck on)
How can I write a notifier that triggers every second, and automatically update the seekbar binding? This notifier would need to live in the PCL with the Command Objects so that it can work cross platform. I'm struggling with getting started on this... I'm not sure where to create it or how to wire it up.
automatically update based on the track's current progress (this one I'm stuck on)
You should be able to do this using a binding to a View property. Here's some pseudo code:
In the ViewModel, add the SeekPosition property:
public double SeekPosition { /* normal INPC get/set */ }
In the View:
add a property and event pair like:
public event EventHandler CurrentPositionChanged;
public double CurrentPosition
{
get { return _mediaPlayer.CurrentPosition; }
set
{
_mediaPlayer.SeekTo(value);
}
}
add a timer to fire the CurrentPositionChanged event on the UI thread. For Android, create this timer in OnResume and destroy it in OnPause.
add a binding in OnCreate:
public override void OnCreate(args)
{
// normal base call and inflate
// ...
var set = this.CreateBindingSet<MyView, MyViewModel>();
set.Bind(this).For(v => v.CurrentPosition).To(vm => vm.SeekPosition);
set.Apply();
}
Note that this approach doesn't use a timer in the PCL. This is because other platforms like iOS and Windows shouldn't need the timer - as they should be able to use progress callbacks/events from the media players on those platforms instead.
I have made a Base Form which is inherited by most Forms in the application. Base form contains a Status Bar Control that displays user name which is internally a static string. User can Switch User at any point in the application by pressing a button on status bar. At this point the user name in the status bar should also change, as if now it only changes in code and UI has no idea about the change. I have googled around and found that i need to bind the label with that static string by implementing a INotifyProperty Interface. I have implemented many example code without success.
Appreciate any help
use BindableAttribute for the property you want to bind a control to it.
[Bindable(true)]
public int Username {
get {
// Insert code here.
return 0;
}
set {
// Insert code here.
}
}
You must implement a class to notify prop changed and therefore the prop can not be static. Combine with a singleton pattern and you have yout solution.
public class Global : INotifyPropertyChanged
{
private string _userName;
public string UserName
{
get
{
return this._userName;
}
set
{
if (this._userName == value)
{
return;
}
this._userName = value;
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs("UserName"));
}
{
}
public event PropertyChangedEventHandler PropertyChanged;
private Global() {}
public static readonly Global Get = new Global();
}
Usage:
var currUserName = Global.Get.UserName;
Global.Get.PropertyChanged += (s, e) => Console.WriteLine(e.PropertyName);
Global.Get.UserName = "John";
And bind to Global.Get to property UserName.
I would:
1- Add a timer to the base form to update the status bar. (the timer resolution is uo to your requirement).
the timer Tick handler would be something like this:
private void timerStatusUpdate_Tick(object sender, EventArgs e)
{
toolStripStatusLabelMessage.Text = StatusMessage();
}
2 - Add a virtual StatusMessage method to your base class:
class BaseForm : Form
{
.......
public virtual string StatusMessage()
{
return "override me!";
}
}
3- override StatusMessage in all your derived classes
class XXXForm : BaseForm
{
........
public override string StatusMessage()
{
return "XXXForm status message";
}
}
I use Reactive Extensions for these things
For example if you have a Context class with a property UserName
you could do this
public static class Context
{
public static Subject<string> UserChanged = new Subject<string>();
private static string user;
public static string User
{
get { return user; }
set
{
if (user != value)
{
user = value;
UserChanged.OnNext(user);
}
}
}
}
And then on your forms just do
Context.UserChanged.ObserveOn(SynchronizationContext.Current)
.Subscribe(user => label.Text = user);
The ObserveOn(SynchronizationContext.Current) makes it safe for cross thread operation calls
I have a multi threaded wpf application with various HW interfaces.
I want to react to several HW failures that can happen.
For example :
one of the interfaces is a temperature sensor and i want that from a certain temp. a meesage would appear and notify the user that it happened.
i came up with the follwing design :
/// <summary>
/// This logic reacts to errors that occur during the system run.
/// The reaction is set by the component that raised the error.
/// </summary>
public class ErrorHandlingLogic : Logic
{
}
the above class would consume ErrorEventData that holds all the information about the error that occurred.
public class ErrorEventData : IEventData
{
#region public enum
public enum ErrorReaction
{
}
#endregion public enum
#region Private Data Memebers and props
private ErrorReaction m_ErrorReaction;
public ErrorReaction ErrorReactionValue
{
get { return m_ErrorReaction; }
set { m_ErrorReaction = value; }
}
private string m_Msg;
public string Msg
{
get { return m_Msg; }
set { m_Msg = value; }
}
private string m_ComponentName;
public string ComponentName
{
get { return m_ComponentName; }
set { m_ComponentName = value; }
}
#endregion Private Data Memebers and props
public ErrorEventData(ErrorReaction reaction, string msg, string componenetName)
{
m_ErrorReaction = reaction;
m_Msg = msg;
m_ComponentName = componenetName;
}
}
the above ErrorHandlingLogic would decide what to do with the ErrorEventData sent to him from various components of the application.
if needed it would be forwarded to the GUI to display a message to the user.
so what do you think is it a good design ?
thanks,
Adiel.
It seems fair enough, however, in terms of design I would have probably just went with a standard Event with custom event args.
Here is an example:
public interface IEventData
{
ErrorReaction Reaction { get; }
string Message { get; }
ComponentName { get; }
}
public class HardwareChangeEventData : IEventData
{
public HardwareChangeEventData(ErrorReaction reaction, string msg, string componentName)
{
Reaction = reaction;
Message = msg;
ComponentName = componentName;
}
public ErrorReaction Reaction { get; private set; }
public string Message { get; private set; }
public ComponentName { get; private set; }
}
....
// introduce a base class so all hardware components can raise the event
public class HardwareComponent
{
public delegate void HardwareChangedEventHandler(IEventData ed);
public event HardwareChangedEventHandler HardwareChanged;
//event-invoking method that derived classes can override.
protected virtual void OnHardwareChanged(IEventData ed)
{
HardwareChangedEventHandler handler = HardwareChanged;
if (handler != null)
{
handler(this, ed);
}
}
}
public class TemperatureGauge : HardwareComponent
{
public void Monitor()
{
// example logic
while (...)
{
if (Temperature < LowThreshold)
{
IEventData ed = new HardwareChangeEventData(ErrorReaction.IncreaseTemp, "Temperature too low!", "TemperatureGauge");
OnHardwareChanged(ed);
}
}
}
public override OnHardwareChanged(IEventData ed)
{
// do something with ed internally (if applicable)
// forward event on to base so it can be passed out to subscribers
base.OnHardwareChanged(ed);
}
}
Above code looks fine.
But for notifying different components , i would say look for Observer pattern ( Event/Deleagte)
if you are going to handle error in WPF why don't use validators for that? see this acticle