So far i separated action with Command Binding. but i need to implent many thing to work same as built-in event. ex, OnClick, OnChange and so on. the MVVM pattern only means is no code behind in ViewModel class. all thing like Component. instead make all thing from scratch. why not just make Event in ViewModel implementation like component. so just attacch and detach that event collection class. is there new way to do this? so far i can think is encapsulate control with event and inhrited to viewmodel class.
public class Utility
{
public static string Manipulation(string search)
{
return search + "Reesult";
}
}
public class CustomWindow : Window
{
protected virtual void SearchBtn(object sender, RoutedEventArgs e)
{
MessageBox.Show( Utility.Manipulation(search.Text) );
}
}
public partial WindowViewModel : CustomWindow
{
public WindowViewModel
{
InitializeComponent();
}
}
<TextBox x:Name="search" />
<Button Click="SearchBtn"> Search </Button>
Related
I have two identical views View1.xaml and View2.xaml and they both have a button button1 and a textfield textfield1. The idea is that when you press the button, the corresponding textfield is filled with some information. Both views use the same method for filling in the textfield (the views are literally identical in that sense).
My question is: how to write generic code using OOP principles and not break the MVVM pattern? My current way of performing this with RelayCommand:
The same code for ViewModel1 and ViewModel2:
public RelayCommand ButtonCommand { get; private set; }
#Constructor
ButtonCommand = new RelayCommand(ExecuteButtonCommand, CanExecuteButtonCommand);
#EndConstructor
private void ExecuteButtonCommand(object message)
{
//Some method to fill the corresponding textfield
}
private bool CanExecuteButtonCommand(object message)
{
return true;
}
Binding for the button in View1.xaml and View2.xaml:
<Button Command="{Binding Path=ButtonCommand, Mode=OneWay}" />
This is bad, because I have to write the same code for both ViewModels. I was trying to make a class ButtonCommand which inherits from RelayCommand, but because not every view will have this functionality, I can't achieve it using this method.
Rather than having a "Base" view model and two derived view models, have your two view models both use the same code defined elsewhere (ideally, both calling the same interface, injected with dependency injection).
This is the Composition over Inheritance principle.
When you're writing your tests, test that both view models call the interface, and test that the implementation of that interface does what it is supposed to do once.
This way, not only can you avoid writing your code twice, you can also avoid testing it twice, and it also allows you to follow other principles like the single responsibility principle.
This could be an way to go:
1 - Create a base viewmodel class:
public class YourBaseViewModel
{
public Object YourBaseProperty{get; set;}
public RelayCommand ButtonCommand { get; private set; }
private void ExecuteButtonCommand(object message)
{
//Some method to fill the corresponding textfield
}
private bool CanExecuteButtonCommand(object message)
{
return true;
}
}
2 - Inherit from the base viewmodel:
public class ViewModel1:YourBaseViewModel
{
// ....
}
public class ViewModel2:YourBaseViewModel
{
// ....
}
EDIT:
If you have another base class you could do:
public class YourBaseViewModel:YourReallyBaseViewModel
{
// ....
}
public class ViewModel1:YourBaseViewModel
{
// ....
}
public class ViewModel2:YourBaseViewModel
{
// ....
}
This is an XY problem. You're asking for a way to solve Y (not duplicate the same ButtonCommand but in actuality), your problem is X (you already have duplication in your code)
I have two identical views View1.xaml and View2.xaml
I'd like to add, that you've also stated you don't have only two identical views, there's more.
The best way to resolve this is to have a parent ParentViewModel that can construct the child ViewModels
So first, we'll need an interface for the child view model
IMyViewModel
public interface IMyViewModel
{
void Load();
}
Next, the implementation
MyViewModel
public class MyViewModel : ViewModelBase, IMyViewModel
{
public MainViewModel()
{
ButtonCommand = new RelayCommand(ExecuteButtonCommand, CanExecuteButtonCommand);
}
public RelayCommand ButtonCommand { get; private set; }
public void Load()
{
//Example load logic
InvalidateCommands();
}
private void InvalidateCommands()
{
ButtonCommand.RaiseCanExecuteChanged();
}
private void ExecuteButtonCommand(object message)
{
//Some method to fill the corresponding textfield
}
private bool CanExecuteButtonCommand(object message)
{
return true;
}
}
And lastly the ParentViewModel which has the responsibility of creating the view models. Please note, I did not tell it WHEN to create the ViewModels, I will leave that up to you.
Parent View Model
public class ParentViewModel : ViewModelBase
{
private Func<IMyViewModel> _myVmCreator;
public ParentViewModel(Func<IMyViewModel> myVmCreator)
{
_friendEditVmCreator = friendEditVmCreator;
}
public ObservableCollection<IMyViewModel> MyViewModels { get; private set; }
private IMyViewModel CreateAndLoadMyViewModel()
{
var myVm = _myVmCreator();
MyViewModels.Add(myVm);
myVm.Load();
return myVm;
}
}
This will allow you to create any number of MyViewModels, or any other type of ViewModel as long as it implements IMyViewModel.
The above example is derived from this course : https://www.pluralsight.com/courses/wpf-mvvm-test-driven-development-viewmodels
I highly recommend it.
I'm not sure how to make navigation using mvvm. I'm a beginner so I haven't used any framework like mvvm light.
I found good example https://rachel53461.wordpress.com/2011/12/18/navigation-with-mvvm-2/. But it is not exactly what I'm looking for because in my app each view will cover all window. So when I will change page i will have no controls access from the mainview.
So I decided to make one MainViewModel for changing ViewModels (as in Rachel Blog) but each ViewModel should know about MainViewModel to execute change view. So when I create PageViewModel, I pass in constructor MainViewModel with public method, for example, changeview().
Is it a good way of doing this? Or, maybe, there's a better way to achieve this?
The child viewmodels should not know about main viewmodel.
Instead they should raise events with names like Forward or Back and so forth. ChangeView is the only example you give, so we’ll go with that.
We'll have the child viewmodel expose commands that cause the events to be raised. Buttons or MenuItems in the child view's XAML can bind to the commands to let the user invoke them. You can also do that via Click event handlers calling viewmodel methods in the child view code behind, but commands are more "correct", because at the cost of a little more work in the viewmodel, they make life a lot simpler for creators of views.
Main viewmodel handles those events and changes the active page viewmodel accordingly. So instead of child calling _mainVM.ChangeView(), child raises its own ChangeView event, and the main VM’s handler for that event on the child calls its own method this.ChangeView(). Main VM is the organizer VM, so it owns navigation.
It’s a good rule to make code as agnostic as possible about how and where it’s used. This goes for controls and viewmodels. Imagine if the ListBox class required the parent to be some particular class; that would be frustrating, and unnecessary as well. Events help us write useful child classes that don’t need to know or care anything about which parent uses them. Even if reuse isn’t a possibility, this approach helps you write clean, well-separated classes that are easy to write and maintain.
If you need help with the details, provide more code, and we can go through applying this design to your project.
Example
public class MainViewModel : ViewModelBase
{
public MainViewModel()
{
FooViewModel = new FooViewModel();
FooViewModel.Back += (object sender, EventArgs e) => Back();
}
public FooViewModel FooViewModel { get; private set; }
public void Back()
{
// Change selected page property
}
}
public class FooViewModel : ViewModelBase
{
public event EventHandler Back;
private ICommand _backCommand;
public ICommand BackCommand {
get {
if (_backCommand == null)
{
// It has to give us a parameter, but we don't have to use it.
_backCommand = new DelegateCommand(parameter => OnBack());
}
return _backCommand;
}
}
// C#7 version
public void OnBack() => Back?.Invoke(this, EventArgs.Empty);
// C# <= 5
//protected void OnBack()
//{
// var handler = Back;
// if (handler != null)
// {
// handler(this, EventArgs.Empty);
// }
//}
}
// I don't know if you already have a DelegateCommand or RelayCommand class.
// Whatever you call it, if you don't have it, here's a quick and dirty one.
public class DelegateCommand : ICommand
{
public DelegateCommand(Action<object> exec, Func<object, bool> canExec = null)
{
_exec = exec;
_canExec = canExec;
}
Action<object> _exec;
Func<object, bool> _canExec;
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return _canExec == null || _canExec(parameter);
}
public void Execute(object parameter)
{
if (_exec != null)
{
_exec(parameter);
}
}
}
How to invoke BackCommand from child XAML:
<Button Content="Back" Command="{Binding BackCommand}" />
I've been working a lot with WPF, and after awhile the MainWindow class becomes cluttered and unorganized. Is there a way to store all of the control events in a custom class like below? Inheriting doesn't work and i'm guessing its because it has no instance of the new class to go off of.
public partial class MainWindow : Window
{
public class ControlEvents : MainWindow //Custom class
{
private void Abutton_Click(object sender, RoutedEventArgs e)
{
...Stuff
}
}
}
Is there a way to store all of the control events in a custom class like below?
No, the event handlers themselves must be defined in the code-behind of the same view class where the element is defined and the handler is hooked up.
You could move the code inside the event handlers to another class though:
public partial class MainWindow : Window
{
private YourClass _handler = new YourObject();
public class ControlEvents : MainWindow //Custom class
{
private void Abutton_Click(object sender, RoutedEventArgs e)
{
_handler.HandleButtonClick(e);
}
}
}
But you should look into MVVM: https://msdn.microsoft.com/en-us/library/hh848246.aspx. There is a reason why this is the recommended design pattern for developing XAML based UI applications.
If you don't use mvvm:
You can create user control for area of controls and load this user control in your main window.
Also - you can take your code of "do stuff" to another class and call it from the event function.
for example:
functions.cs
dostuff1()
{
...
}
dostuff2()
{
...
}
your usercontrol/mainwindow.xaml.cs:
functions f = new functions();
private void Abutton_Click(object sender, RoutedEventArgs e)
{
f.dostuff1();
}
good luck
You can move all the events to Partial class in separated file.
call the file MainWindowEvents.cs or something. (to remember what is it)
I'm making Windows 10 UWP app.
To make it simple, let's say I have a TextBox somewhere on my page. Under this TextBox there are my 3 (different types) custom controls. These 3 controls have the same property and event that is raised if user clicks a button inside one of these controls, and the method to which this event is subscribed to causes that the text inside a TextBox is assigned to SomeProperty in the control.
Is there some way to subscribe to these events, so that I don't have to have 3 different methods handling it while they're literally doing the same thing, but for 3 different controls.
This is the code that all of the controls have in common. There are MyControlA, MyControlB and MyControlC
public sealed partial class MyControlA : UserControl
{
public delegate void StartPositionClickedEventHandler(object sender, EventArgs e);
public event StartPositionClickedEventHandler StartPositionClicked;
public string SomeProperty
{
get { return (string)GetValue(SomePropertyProperty); }
set { SetValue(SomePropertyProperty, value); }
}
private void StartPosition_Click(object sender, RoutedEventArgs e)
{
StartPositionClicked?.Invoke(this, EventArgs.Empty);
}
// Using a DependencyProperty as the backing store for SomeProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SomePropertyProperty =
DependencyProperty.Register("SomeProperty", typeof(string), typeof(MyControlA), null);
}
I subscribe to the event in the page where the control is
_control.StartPositionClicked += new MyControlA.StartPositionClickedEventHandler(SetCurrentStartPositon);
and handle it
private void SetCurrentStartPositon(object sender, EventArgs e)
{
_control.SomeProperty = DummyTextBox.Text;
}
And I would have to do that for all of the controls, but I might have lots of them as they're created programmatically as they're needed, so I wish to have some better way of doing it. Everything I thought of seems to be dumb or super inefficient.
Thank you in advance!
Since all your controls have the same properties of the same type, there are two ways how to achieve what you want:
1: Creating a base class that will implement the code they all have in common and make all the controls inherit from it:
public abstract class MyControlBase : UserControl
{
public delegate void StartPositionClickedEventHandler(object sender, EventArgs e);
public event StartPositionClickedEventHandler StartPositionClicked;
public string SomeProperty
{
get { return (string)GetValue(SomePropertyProperty); }
set { SetValue(SomePropertyProperty, value); }
}
protected void StartPosition_Click(object sender, RoutedEventArgs e)
{
StartPositionClicked?.Invoke(this, EventArgs.Empty);
}
// Using a DependencyProperty as the backing store for SomeProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SomePropertyProperty =
DependencyProperty.Register("SomeProperty", typeof(string), typeof(MyControlA), null);
}
public sealed partial class MyControlA : MyControlBase
{
// Class specific code
}
Please note that you'll also need to change the base class in XAML so instead of something like
<UserControl>
<!-- Your XAML code -->
</UserControl>
you have to use something like this assuming xmlns:local points to the namespace in which MyControlBase is:
<local:MyControlBase>
<!-- Your XAML code -->
</local:MyControlBase>
Then you can make one event handler that will look similar to this:
private void SetCurrentStartPositon(object sender, EventArgs e)
{
// Get the control that invoked this event
MyControlBase senderControl = (MyControlBase)sender;
senderControl.SomeProperty = DummyTextBox.Text;
}
2: Creating a simple interface for them like this and make all the three controls inherit from it:
public interface IMyControl
{
string SomeProperty { get; set; }
}
So in your code, you could subscribe events of all the controls to one method that will look like this one:
private void SetCurrentStartPositon(object sender, EventArgs e)
{
// Get the control that invoked this event
IMyControl senderControl = (IMyControl)sender;
senderControl.SomeProperty = DummyTextBox.Text;
}
I'd suggest using the first option since it makes your code simplier to read and avoids possible inconsistencies from changing the common code.
Yes you can!
It is called EventSetter and you can use it like this:
<Style TargetType="{x:Type local:MyControlA}">
<EventSetter Event="StartPositionClicked" Handler="SomeAction"/>
</Style>
You need to put this style in the parent control i.e. Grid or StackPanel and also you need to implement the SomeAction method.
HTH
Following MVVM, I have an object persisted with the existence of a UI Window, object defined in XAML. This object represents the ModelView so it contains the controls which can modify the model. I am finding myself calling FrameworkElement.FindResource("myResource") for every user control. What is the proper way to grab the instance of this object?
XAML:
<p:MyModelView x:Key="modelView" />
CodeBehind:
//for every control I call:
public void SomeEventHandler(object _sender, EventArgs _someEventArgs) {
MyModelView repeatedCode= this.FindResource("modelView")
repeatedCode.DoSomeModificationRelatedToControl(args[] someArgs);
}
If you need your ViewModel a lot of places in your View code-behind, create and keep the ViewModel in a variable in the code-behind instead of creating it as a resource in your Xaml. For example:
public partial class MainWindow : Window
{
private MainViewModel _vm;
public MainWindow()
{
InitializeComponent();
_vm = new MainViewModel()
{
Name = "MyViewModel",
...
};
this.DataContext = _vm;
}
That last line is important - by making the ViewModel the View's DataContext, you can bind to it in Xaml like normal.
Now, your event handlers get at least a line or two shorter:
public void SomeEventHandler(object sender, EventArgs someEventArgs)
{
_vm.DoSomeModificationRelatedToControl(someArgs);
}