WPF - Best way to initialize Notification Icon without breaking MVVM standards - c#

I am fairly new to the MVVM Architecture, but have developed a handful of applications using MVVM and the Microsoft Community Toolkit Library.
I am currently developing an Event Driven application which monitors various different LogFiles and if an issue is flagged a Custom Notification Icon pops up on the users screen.
I want to abide fully by MVVM Standards, but am struggling to figure out how I could instantiate the Notification User Controls without doing this from a VM.
Due to the fact each Notification User control will look the same, and have the same information within it, I have opted to use one Viewmodel for each Notification Control. This is where the Problem arises.
public MainNotifcationViewModel(NotificationHub notiHub, TaskbarIcon tb)
{
//Taskbar Definition
_tb = tb;
//Notification Hub Definition
_notiHub = notiHub;
notiHub.NotificationRecieved += NotiHub_NotificationRecieved;
Debug.WriteLine(notiHub.listOfFileMonitors.Count);
WeakReferenceMessenger.Default.Register<ChangeConfigMessage>(this, (r, m) =>
{
GetConfig(m.Value);
});
}
[RelayCommand]
private async Task GetConfig(Configuration value)
{
Debug.WriteLine(value);
_notiHub = new NotificationHub(value);
//Restart Notification Hub with new Config
}
private void NotiHub_NotificationRecieved(object? sender, Noti.Notification.Hub.Data_Models.Notification.NotificationDetails e)
{
switch (e.Type)
{
case NotiType.Logfile:
Application.Current.Dispatcher.Invoke(new Action(() => {
_tb.ShowCustomBalloon(new LogfileNotification(e), PopupAnimation.Scroll, 5000);
}));
break;
}
}
Above, you will see the Viewmodel I have instantiated (within the App.Xaml of my application, instead of as a DataContext, for testing purposes). This Viewmodel Triggers the "Monitoring Part" of my application, which waits for changes to a file, and triggers the notiHub.NotificationRecieved Event. This Event then triggers a method, which displays my custom Notification Icon.
I have got this to work Flawlessly by passing in the event details into the UserControl via a Constructor (See code below).
public partial class LogfileNotification : UserControl
{
public LogfileNotification(NotificationDetails notiDetails)
{
TimeStamp = notiDetails.TimeRecieved.ToLongTimeString();
NotiType = notiDetails.Type.ToString();
Message = notiDetails.Message;
Status = notiDetails.Status.ToString();
NotiName = notiDetails.Name;
InitializeComponent();
}
public string NotiType { get; set; }
public string TimeStamp { get; set; }
public string Message { get; set; }
public string Status { get; set; }
public string NotiName { get; set; }
}
The UserControl can takes then displays these values within the Notification (by setting it's code behind as the DataContext).
DataContext="{Binding RelativeSource={RelativeSource Self}}"
The Problem I have with this part of the application, is that is does not abide at all by MVVM Standards. Due to what would be the ViewModel, calling the View.
Has anyone got any ideas on how I could display these Notification UserControls, and set a dataContext for them, without breaking any MVVM Standards.
Any Suggestions would be appreciated :D

Related

Xamarin android with mvvm light : Textview binding only updates when a relaycommand is called

I setup a very simple app to get me started and trying things out. It has a label and two buttons. The buttons are linked to relaycommands in the main view model are used to send a message to a server using Mqtt which work as intended. The label is used to show part of the data received from the server. Everything seems to work fine except the labels won't update as soon as the message is received event though I can see in debugging that the property is set. The label will update as soon as I press one of the two buttons...
I'm new to the whole Xamarin android thing and have used mvvm light once in a WPF application.
Main Activity :
public partial class MainActivity
{
// UI Elements
public TextView ScanInfoLabel { get; private set; }
public Button UnlockButton { get; private set; }
public Button RegisterButton { get; private set; }
// Keep track of bindings to avoid premature garbage collection
private readonly List<Binding> _bindings = new List<Binding>();
// Get view model
private MainViewModel mainViewModel { get { return App.Locator.Main;}}
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
// Set view from the "main" layout resource
SetContentView(Resource.Layout.Main);
// Get the UI elements by ID
ScanInfoLabel = FindViewById<TextView>(Resource.Id.ScanInfoLabel);
UnlockButton = FindViewById<Button>(Resource.Id.UnlockButton);
RegisterButton = FindViewById<Button>(Resource.Id.RegisterButton);
// Set Bindings for textviews
_bindings.Add(
this.SetBinding(
() => mainViewModel.ScanInfoLabel,
() => ScanInfoLabel.Text));
// Set the bindings for commands
UnlockButton.SetCommand("Click", mainViewModel.UnlockCommand);
RegisterButton.SetCommand("Click", mainViewModel.RegisterTagCommand);
}
In Main view model :
// RelayCommands
public RelayCommand UnlockCommand;
public RelayCommand RegisterTagCommand;
public RelayCommand MqttConnectCommand;
// Bindable properties
private string _scanInfoLabel = "Test";
public string ScanInfoLabel
{
get { return _scanInfoLabel; }
set { Set(ref _scanInfoLabel, value); }
}
// New scan message received
private void RFIDScanReceived(RFID.Scan scan)
{
ScanInfoLabel = BitConverter.ToString(scan.UID);
}
I would expect the label to show the data as soon as the mqtt message is received (which is then sent to the mainviewmodel using Messenger.Default.send<>() from mvvm light). But nothing is changed in the UI until I click on one of the buttons and then the correct information in displayed.
I don't really know where to start being new to xamarin android and none of my searches seemed to be of any help.
Any help will be appreciated, thanks!
ViewModels generally implement the INotifyPropertyChanged interface,
which means that the class fires a PropertyChanged event whenever one
of its properties changes. The data binding mechanism in Xamarin.Forms
attaches a handler to this PropertyChanged event so it can be notified
when a property changes and keep the target updated with the new
value.
Solution:
Make you model inherit from INotifyPropertyChanged and add PropertyChanged inside the set part. Then the labels will update as soon as the value of ScanInfoLabel changed.
public class BaseViewModel : INotifyPropertyChanged
{
// Bindable properties
private string _scanInfoLabel = "Test";
public string ScanInfoLabel
{
get { return _scanInfoLabel; }
set
{
_scanInfoLabel = ScanInfoLabel;
PropertyChanged(this, new PropertyChangedEventArgs("ScanInfoLabel"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
You can refer:data-bindings-to-mvvm
Well I found out it was a threading issue since I set the property from a Messenger call. This apparently is not an issue in WPF which is why I was a bit stuck but using the DispatcherHelper did the trick.
// New scan message received
private void RFIDScanReceived(RFID.Scan scan)
{
DispatcherHelper.CheckBeginInvokeOnUI(() =>
{
ScanInfoLabel = BitConverter.ToString(scan.UID);
});
}

Xamarin Forms MVVM with an actual model

I'm fairly new to Xamarin and stumbled across MVVM and really like it as an architectural pattern. However, I found that most HowTo's and tutorials out there only address the VVM (i.e. View-ViewModel) side of things, probably for simplicity sake!?
I would like to know how the communication between a ModelView and its associated models takes place using the INotifyPropertyChanged paradigm and other things.
If I understand correctly, I personally would put stuff like data handling, data storage (collections), db connections and stuff like that into a model. At least this is how I would've been doing it in the good old MVC days. Following questions arouse in my mind:
Where do I create the model(s) and how do I assign them to ViewModels?
How do I properly connect Model and ViewModel such that property updates are propagated and can be handled correctly?
Would you set the model as a member of the ViewModel?
In my current example, I would like to implement a SensorModel which provides several sensory data which layers above can subscribe to. I would like to send updates whenever new sensor data is available to the layers above; i.e. a ViewModel, for instance.
I'd basically had something like this in mind:
class Sensor
{
int _id { get; set; }
string _name { get; set; }
}
class SensorModel
{
private List<Sensor> _sensors { get; set; }
public void addSensor(Sensor s) ...
public void removeSensor(Sensor s) ...
}
Does anybody have links to actual/complete MVVM examples, including the connection between Model and ViewModel?
Any help appreciated.
Use Lastest stable Xamarin Forms
MODELS
In the Project, create a Models folder
To store data, i usually use SQLite or a temp store:
class DataStore
{
public static List<SensorModel> SensorStore { get; set; }
}
Create the SensorModel model
class SensorModel
{
internal int Id { get; set; }
internal string Sensor { get; set; }
}
VIEWMODELS
In the Project, create a ViewModels folder
Create a SensorVM viewmodel
class SensorVM : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public System.Windows.Input.ICommand StartCommand { get; set; }
public string SensorName { get; set; }
public SensorVM()
{
DataStore.SensorStore = new List<SensorModel>();
StartCommand = new Xamarin.Forms.Command(StartSubmit);
}
private void StartSubmit(object paramter)
{
var sensor = new SensorModel()
{
Id = 1,
Sensor = SensorName
};
AddSensor(sensor);
}
public void AddSensor(SensorModel sensor)
{
//do something
DataStore.SensorStore.Add(sensor);
}
}
VIEWS
In the Project, create a Views folder
Create a Sensor.xaml view
<ContentPage.Content>
<StackLayout Spacing="10" Orientation="Vertical">
<Entry Text="{Binding SensorName}" />
<Button Command="{Binding StartCommand}" Text="Start" />
</StackLayout>
</ContentPage.Content>
In the code behind:
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class Sensor : ContentPage
{
SensorVM vm;
public Sensor()
{
InitializeComponent();
BindingContext = vm = new SensorVM();
}
}
Hope that helps.
I would like to know how the communication between a ModelView and its
associated models takes place using the INotifyPropertyChanged
paradigm and other things.
I think the best way to create a communication in MVVM is Messaging Center.
https://learn.microsoft.com/pt-br/xamarin/xamarin-forms/app-fundamentals/messaging-center
It's not coupled from device (sensor) code to view models ...
Your messages, in this model, active events that could acess your viewmodels as well as other structures.
A sample of this
In your view use :
public void MessegingCenterInit()
{
#region Bluetooth
MessagingCenter.Subscribe<string, string>("App", "Status_name", (sender, arg) =>
{
App.PVM.Name = $"{arg}";//using INotifyPropertyChanged and view model
viewmodelMethod();//using only a viewmodel
});
#endregion
}
in your model use:
public string Name
{
get { return name; }
set
{
name = value;
App.PVM.Add_patient.AddCanExecuteChanged();//PVM is a viewmodel
//The view model need to have INotifyPropertyChanged as a interface
}
}
In specific code you have (into a generic method or event):
string new_name = John;
MessagingCenter.Send<string,string>("App","Status_name",new_name);
There are several ways to do it, its a simple one, you can try use objects as sender with less information.
Regards
Xamarin itself gives a really good example with their default Master-Detail Solution.
Just create a new Xamarin.Forms App and select the Master-Detail Layout.
It includes several Views, ViewModels (with the BaseVIewModel) and some MockUp Data Classes.
For a start just have a look around there :)
In almost all cases there is no communication between the Model and ViewModel, and very rarely there is communication between the Model and View. If you need to communicate between Model and ViewModel it is extremely likely that you are doing something wrong.
To explain, your model usually describes some entity, like that you have the class Cat:
public class Cat
{
public string Color {get; set;}
}
It is generally used in ViewModel either as the field or as a Collection like:
public class CatsViewModel
{
public List<Cat> Cats {get; set;}
}
The cat shouldn't be able to update by itself, if it is updated it is done either by bindings with the view or somewhere from ViewModel.
So you have some architectural problems in your app, I think.

Passing data between view models in MVVM C#

I'm currently trying to create a "log" text box that gets messages between multiple view models (tied to multiple views) that I have. I've tried the approach described by user Blachshma here (Multiple Data Contexts in View) but it does not seem to be working.
I have three classes. Class AViewModel, Class BViewModel and Class ABViewModel.
The view for A binds to AViewModel using the following code in its constructor:
this.InitializeComponent();
this.model = new AViewModel();
this.DataContext = this.model;
The view for B and AB follows the same pattern.
The class structures are as follows:
public class A : INotifyPropertyChanged
{
private string log = string.empty;
public class A()
{
}
public string ALog
{
get
{
return this.log;
}
set
{
this.log = value;
this.NotifyPropertyChanged("ALog");
}
}
private void NotifyPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
/* Function that executes when relay command is clicked */
private void ExecuteCommand()
{
this.ALog += "here";
}
}
Class B is defined the same way with property BLog
Class ABViewModel has properties for each other view model
public class ABViewModel
{
public AViewModel AVM
{
get;
set;
}
public BViewModel BVM
{
get;
set;
}
}
In the xaml I simply have
<TextBox Text="{Binding ABViewModel.AVM}" />
My plan is to eventually using Multibinding to concatenate both logs together, but at the moment I can't even get the one View Model to update my string. It looks like my container view model ABViewModel isn't getting updated, but I don't really understand why, but I'm not entirely sure how to fix this.
Any suggestions are extremely appreciated!
Thanks!
Edit:
I debug my code and see that my string ALog is getting updated, but I don't see the change on the UI. For more information, I click a button that's connected to a RelayCommand in class A. This button invokes a method to connect to a COM port. I'm able to use the COM port from other view models successfully after opening it. The log is supposed to update saying that the com port was opened but I never see any text added to the log in the GUI even though the instance of ALog that I can debug through has the added text.
I can't use Prism or MVVM-light for this particular project.

WPF - MVVM Light, Ribbon Control, nested user controls/views and using events between user controls/views

I am building a WPF application with the aid of MVVM light and Unity.
I have a ribbon control in the main form, with one tab without tab category, and two tabs in a tab category. User control 3 includes user control 2 and user control 1.
I would like to make visible the Tab Category and tabs 2 and 3 when I click in user control 2 and make them invisible when click in user control 3 or 1.
I confused a little bit with the event handling in mvvm structure.
I would like to do this properly with the MVVM rules. Could you give me an example or some really helpful directions how to do it?
Check how it looks like
Your Ribbon or tabs for that matter, should be backed up by a ViewModel, let's say TabCategoryViewModel or Tab2ViewModel and Tab3ViewModel. Inside these ViewModels you inject the IMessanger service (of course register it before if not already done) and create a POCO event message, like SelectedViewMessage
public class SelectedViewMessage
{
public string ViewName { get; set; }
}
Inside your TabCategoryViewModel you would register to listen to this message
public class TabCategoryViewModel : ViewModelBase
{
public readonly IMessanger messageService;
public TabCategoryViewModel(IMessanger messageService)
{
if(messageService == null)
{
throw ArgumentNullException("messageService");
}
this.messageService = messageService;
this.messageService.Register<GoToPageMessage>(this, OnSelectedViewChanged);
}
protected void OnSelectedViewChanged(SelectedViewMessage message)
{
this.IsVisible = message.ViewName == "UserControl2";
}
private bool isVisible;
public bool IsVisible
{
get { return isVisible; }
set
{
if(isVisible != value)
{
isVisible = value;
RaisePropertyChanged();
}
}
}
}
You inject the same IMessanger service into your ViewModel you use to Bind your UserControl2 and fire the message via
var message = new SelectedViewMessage {
ViewName = "UserControl2";
};
this.messangerService.Send<SelectedViewMessage>(message);
This code can be placed inside a ViewSelectedCommand or something similar, and you can use Blend Interactivity Triggers/Actions to bind this to certain events on the View/UserControl
This can be achieved by adding event onPropertyChange whenever user clicks on the tab and add that property in xaml under Visibility tag. Also look at Handling UI Control to understand that mapping in xaml and ti understand event handling from Here
Hope it helps.

Passing object to viewmodel

Question on the MVVM pattern where I think I have it wrong.
When a touchdown event occurs in a view I want to popup a message i.e.:
private void marker_TouchDown(MessageObject msgData)
{
CustomMessageControl message = new CustomMessageControl() {Width = 610, Height = 332};
CustomMessageViewModel messageVM = new CustomMessageViewModel(msgData);
message.DataContext = messageVM;
//Add to canvas
}
My viewmodel:
public class CustomMessageViewModel
{
public MessageObject message { get; set; }
public CustomMessageViewModel(MessageObject message)
{
this.MessageObject = message;
}
}
This works but doesn't feel right. Is this an acceptable way to populate the view model?
I believe that you are violating MVVM in creating the control in the view model. This isn't testable, your view model has to create the control now and that shouldn't be a requirement for testing (this emphasizes the lack of the separation of concerns between the UI and the view model).
Instead of creating the control, it is completely acceptable for your view model to fire an event of it's own. In this event, you'd pass the view model that you want the dialog/overlay/control to bind to, something like this:
public class CustomMessageControlEventArgs : EventArgs
{
public CustomMessageViewModel CustomMessageViewModel { get; set; }
}
public event EventHandler<CustomMessageControlEventArgs>
ShowCustomMessageControl;
private void marker_TouchDown(MessageObject msgData)
{
// Create the view model.
var message = ...;
// Get the events.
var events = ShowCustomMessageControl;
// Fire.
if (events != null) events(this,
new CustomMessageControlEventArgs {
MessageObject = new CustomMessageViewModel(msgData)
});
}
Then, in your UI code, you would bind to the event and then show the appropriate user interface for that event.
Remember, MVVM isn't strictly about being able to declare everything in XAML or binding data to the UI through just data bindings, it's about proper separation of code.
You want to separate the what of what is displayed (the view model) from the how of what is displayed (the UI); in firing an event, you're maintaining that separation of concerns.
Yes, you'll have to write some code behind (or you could do it through property notification changes, but it's uglier, frankly), but it maintains the separation and allows for easy testability without having to bring in any user interface elements.

Categories