How to make parent view bind command with child view-model method - winforms

I have a simpleButton in parent view and I want to bind this button with a method in child view-model.
_fluentApi.BindCommand(simpleButton , x => x.SaveMessage());

DevExpress MVVM Fluent API supports binding to nested properties/commands:
// ViewModel
public class ParentViewModel {
public ParentViewModel() {
Child = ChildViewModel.Create();
}
public virtual ChildViewModel Child {
get;
protected set;
}
}
public class ChildViewModel{
public static ChildViewModel Create() {
return DevExpress.Mvvm.POCO.ViewModelSource.Create<ChildViewModel>();
}
public void Save(){
// ...
}
}
// View
var fluentApi = mvvmContext.OfType<ParentViewModel>()
fluentApi.BindCommand(simpleButton , x => x.Child.Save());

Related

Update viewmodel based on MainWindow event

I have a UdpClient, firing off a DataRecevied event on my MainWindow:
public partial class MainWindow : Window
{
public static YakUdpClient ClientConnection = new YakUdpClient();
public ClientData;
public MainWindow()
{
InitializeComponent();
Loaded += OnLoaded;
}
private void OnLoaded(object sender, RoutedEventArgs routedEventArgs)
{
ClientData = new ClientData();
ClientConnection.OnDataReceived += ClientConnectionOnDataReceived;
}
private void ClientConnectionOnDataReceived(object sender, MessageEventArgs messageEventArgs)
{
ClientData.Users = messageEvenArgs.ConnectedUsers;
}
}
My ClientData and User classes look as follow:
public class ClientData
{
public List<User> Users {get;set;)
}
public class User
{
public string Name {get;set;}
}
On my MainWindow, I have a UserControl called UserListView which has a ViewModel called UserListViewModel
The ViewModel looks as follow:
public class UserListViewModel: BindableBase
{
public UserListViewModel()
{
//I am sure there are better ways of doing this :(
Users = new ObservableCollection<User>((MainWindow)Application.Current.MainWindow).ClientData.Users
});
private ObservableCollection<User> _users;
public ObservableCollection<User> Users
{
get{ return _users;}
set { this.SetProperty(ref this._users, value); }
}
}
The difficulty I have here, is when the ClientConnectionOnDataReceived event on the MainWindow gets fired, I would like to update my ClientData class, My Viewmodel should then somehow be notified that the list changed, and subsequently update my UI.
Can anyone give me a solid example of how to achieve this using MVVM (Prism) in WPF?
I am new to MVVM, so i am still trying to figure this out.
First of all, there's no obvious reason why the main window should do the subscription.
I'd go for something like this:
create a service that encapsulates the subscription (and subscribes in its constructor)
register that as a singleton
have it implement INotifyPropertyChanged (to notify consumers of a change to Users)
inject the service into UserListViewModel and observe the Users property (see PropertyObserver)
when Users in the service changes, update Users in the user list view model
and best of all, no need for ObservableCollection here :-)
EDIT: example:
interface IUserService : INotifyPropertyChanged
{
IReadOnlyCollection<User> Users
{
get;
}
}
class YakUdpService : BindableBase, IUserService
{
private readonly YakUdpClient _yakUdpClient;
private IReadOnlyCollection<User> _users;
public YakUdpService()
{
_yakUdpClient = new YakUdpClient();
_yakUdpClient.OnDataReceived += ( s, e ) => Users = e.ConnectedUsers;
}
public IReadOnlyCollection<User> Users
{
get
{
return _users;
}
private set
{
SetProperty( ref _users, value );
}
}
}
class UserListViewModel : BindableBase
{
private IReadOnlyCollection<UserViewModel> _users;
private readonly IUserService _userService;
private readonly PropertyObserver<IUserService> _userServiceObserver;
public UserListViewModel( IUserService userService )
{
_userService = userService;
_userServiceObserver = new PropertyObserver<IUserService>( userService );
_userServiceObserver.RegisterHandler( x => x.Users, () => Users = _userService.Users.Select( x => new UserViewModel( x ) ).ToList() );
// ^^^ should use factory in real code
}
public IReadOnlyCollection<UserViewModel> Users
{
get
{
return _users;
}
private set
{
SetProperty( ref _users, value );
}
}
}
and then register the service
Container.RegisterType<IUserService, YakUdpService>( new ContainerControlledLifetimeManager() );
in your bootstrapper or your module's initialization.

wpf mvvm model inside model for parent child relationship and nested views

I am new to MVVM and it is not clear to me what to do when my business logic has a model inside another model for a parent child relationship and I want my Views and ViewModel to represent it.
Meaning I want the child View to be independent and reusable (I use UserControl) and has its own ViewModel and the parent View to have an element of the child and also its own ViewModel
The solution I can think of is to create a wrapper in parent ViewModel that create ViewModel of child from the parent model
Can you tell me if this is how it meant to be? or if there a better best practice to do that?
Here is a Pseudocode of what I mean where student is the parent and teacher is the child
// The child model
class TeacherModel
{
public string teacherName { get; set; }
}
// The parent model (the student has a teacher)
class StudentModel
{
public int studentId { get; set; }
public TeacherModel teacherModel { get; set; }
}
class TeacherViewModel
{
public TeacherModel teacherModel { get; set; }
}
class StudentViewModel
{
public StudentModel studentModel { get; set; }
// Is this good parctice?
public TeacherViewModel wrapperTeacherViewModel
{
get
{
TeacherViewModel tempTeacherViewModel = new TeacherViewModel();
tempTeacherViewModel.teacherModel = studentModel.teacherModel;
return tempTeacherViewModel;
}
}
}
class TecherView : UserControl
{
//TextBox binded to teacherModel.teacherName using Text="{Binding teacherModel.teacherName}"
}
class StudentView : UserControl
{
// TextBox binded to studentModel.studentId using Text="{Binding studentModel.studentId}"
// Is this good parctice?
// TecherView binded to wrapperTeacherViewModel usinng DataContext="{Binding wrapperTeacherViewModel}"
}
class Porgram
{
void main()
{
TeacherModel teacherModel = new TeacherModel();
teacherModel.teacherName = "Marry";
StudentModel studentModel = new StudentModel();
studentModel.studentId = 12345;
studentModel.teacherModel = teacherModel;
StudentViewModel studentViewModel = new StudentViewModel();
studentViewModel.studentModel = studentModel;
StudentView studentView = new StudentView();
studentView.DataContext = studentView;
}
}
I noticed an issues with solution. If i want my inner view model (Teacher) to have a property that does not exists in the model for example "enabled". That is because I never create TeacherViewModel instance myself so I do not have a chance to set something like teacherViewModel.enabled = false

How to close dialog window from viewmodel (Caliburn+WPF)?

I haveViewModel1 and View1 associated with it. I start dialog window from ViewModel2 (some another viewmodel) using IWindowManager object. The code from ViewModel2 class:
windowManager.ShowDialog(new ViewModel());
So, I have Dialog Window with View1 user control.
My answer is next - I can close that dialog window using red close button, but how to close it using my specific button (contained in View1 user control), something like "Cancel" button with close command (Command={Binding CancelCommand}), CancelCommand of course is contained in ViewModel1 class.
It's even easier if your view model extends Caliburn.Micro.Screen:
TryClose();
You can get the current view (in your case the dialog window) with implementing the IViewAware interface on your ViewModel. Then you can call Close on the the view (the Window created as the dialog) when your command is executed.
The easiest why is to derive from ViewAware:
public class DialogViewModel : ViewAware
{
public void ExecuteCancelCommand()
{
(GetView() as Window).Close();
}
}
If you are not allowed to derive you can implement it yourself:
public class DialogViewModel : IViewAware
{
public void ExecuteCancelCommand()
{
dialogWindow.Close();
}
private Window dialogWindow;
public void AttachView(object view, object context = null)
{
dialogWindow = view as Window;
if (ViewAttached != null)
ViewAttached(this,
new ViewAttachedEventArgs(){Context = context, View = view});
}
public object GetView(object context = null)
{
return dialogWindow;
}
public event EventHandler<ViewAttachedEventArgs> ViewAttached;
}
Note: I've used Caliburn.Micro 1.3.1 for my sample.
A cleaner way (Subject of personal taste) that I use alot is to use the IResult pattern, this way you abstract the Window implemenation
Viewmodel
public IEnumerable<IResult> CloseMe()
{
yield return new CloseResult();
}
Result code
public class CloseResult : Result
{
public override void Execute(ActionExecutionContext context)
{
var window = Window.GetWindow(context.View);
window.Close();
base.Execute(context);
}
}
public abstract class Result : IResult
{
public virtual void Execute(ActionExecutionContext context)
{
OnCompleted(this, new ResultCompletionEventArgs());
}
protected virtual void OnCompleted(object sender, ResultCompletionEventArgs e)
{
if (Completed != null)
Completed(sender, e);
}
public event EventHandler<ResultCompletionEventArgs> Completed;
}
edit (Only needed for IoC): If you wanna take it a step further you do a base class for all screens
public abstract class ShellPresentationModel : Screen
{
public ShellPresentationModel(IResultFactory resultFactory)
{
Result = resultFactory;
}
public IResultFactory Result { get; private set; }
}
This way you can inject dependencies with a IoC much easier, then your VIewmodel close method will look like this
public IEnumerable<IResult> CloseMe()
{
yield return Result.Close();
}
An example on a IResult that uses dependency can be
public class ShowDialogResult<TModel> : Result
{
private readonly IWindowManager windowManager;
private readonly TModel model;
private Action<TModel> configure;
public ShowDialogResult(IWindowManager windowManager, TModel model)
{
this.windowManager = windowManager;
this.model = model;
}
public IResult Configure(Action<TModel> configure)
{
this.configure = configure;
return this;
}
public override void Execute(ActionExecutionContext context)
{
if(configure != null)
configure(model);
windowManager.ShowDialog(model);
base.Execute(context);
}
}
edit Just noticed that i forgot to add an example of the above IoC exmaple, here goes
With a child IoC container pattern it would look like this
public IEnumerable<IResult> ShowDialog()
{
yield return Result.ShowDialog<MyViewModel>();
}
Without a child container pattern you would need to inject parent dependeync into the child manually
yield return Result.ShowDialog<MyViewModel>().Configure(m => m.SomeData = this.SomeData);

Galasoft Messenger Show Window

I want to open a window from my ViewModel.
How can I create and show it using Galasoft Messenger?
public partial class View {
public View() {
InitializeComponents();
//Register Open message
}
//This is called when ViewModel sends a message
public void OpenView() {
new View().Show();
}
}
public class ViewModel {
public ViewModel() {
//Send message to open some view
}
}
This situation does not require an object to be passed from ViewModel to View; therefore, just registering of type object, passing null, BUT the token is key.
public partial class View {
public View() {
InitializeComponents();
//Register Open message BEFORE ViewModel calls Messenger.Default.Send
Messenger.Default.Register<object>(this, ViewModel.OpenViewToken, p => { OpenView(); });
}
//This is called when ViewModel sends a message
public void OpenView() {
new View().Show();
}
}
public class ViewModel {
public static readonly Guid OpenViewToken = Guid.NewGuid();
public ViewModel() {
Messenger.Default.Send<object>(null, OpenViewToken);
}
}

Winform dialog with WPF window as Parent

I have a WinForm dialog and I want to set its Parent property to a WPF window.
How can I do this?
Consider passing parameter to ShowDialog method instead of using Parent property.
You can write helper class
class Wpf32Window : IWin32Window
{
public IntPtr Handle { get; private set; }
public Wpf32Window(Window wpfWindow)
{
Handle = new WindowInteropHelper(wpfWindow).Handle;
}
}
public static class WindowExtensions
{
public static IWin32Window GetWin32Window (this Window parent)
{
return new Wpf32Window(parent);
}
}
After that you can just write
winFormsWindow.Show(yourWpfWindow.GetWin32Window());

Resources