I have a WPF project in Windows 2012 in which I need to load some information in the Window Loaded event. I need to do this in the View Model rather than in the CodeBehind, though. I am attempting to use the following code:
In my xaml:
<interactivity:Interaction.Behaviors>
<behaviors:WindowLoadedBehavior LoadedCommand="{Binding WindowLoadedCommand}" />
</interactivity:Interaction.Behaviors>
In my View Model:
private DelegateCommand _WindowLoadedCommand;
public DelegateCommand WindowLoadedCommand
{
get
{
return _WindowLoadedCommand;
}
private set
{
_WindowLoadedCommand = value;
}
}
public ShellViewModel()
{
WindowLoadedCommand = new DelegateCommand(WindowLoadedAction);
}
protected void WindowLoadedAction()
{
...
}
My attached behavior:
public class WindowLoadedBehavior : Behavior<FrameworkElement>
{
[SuppressMessage("Microsoft.StyleCop.CSharp.MaintainabilityRules", "SA1401:FieldsMustBePrivate", Justification = "Dependency Property. Allow public.")]
public static DependencyProperty LoadedCommandProperty = DependencyProperty.Register("LoadedCommand", typeof(ICommand), typeof(WindowLoadedBehavior), new PropertyMetadata(null));
public ICommand LoadedCommand
{
get { return (ICommand)GetValue(LoadedCommandProperty); }
set { SetValue(LoadedCommandProperty, value); }
}
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.Loaded += AssociatedObject_Loaded;
}
protected override void OnDetaching()
{
AssociatedObject.Loaded -= AssociatedObject_Loaded;
base.OnDetaching();
}
private void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
{
if (LoadedCommand != null)
LoadedCommand.Execute(null);
}
}
The OnAttached, AssociatedObject_Loaded and LoadedCommand get are all firing, but the LoadedCommand set is not firing and, obviously, the WindowLoadedCommand isn't firing. Any clue what I can do to get this working?
There are a few options. A couple of them listed here:
how to call a window's Loaded event in WPF MVVM?
However, in the off chance that you or anyone else cares that you are spending several hours to complete a task that should have taken 30 seconds, you might want to try this instead.
public MainWindow()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(MainWindow_Loaded);
}
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
ShellViewModel.Instance.WindowLoadedCommand.Execute(null);
}
Related
I´ve started learning only recently so this is a newbie question.
Maybe someone could help me out in regards to what I´d have to do differently for my code to work.
In short: I have a class that inherits from INotifyPropertyChanged (which I´v tried to implement according to MSDN). When I press a button I want to change a variable in this class which in turn should raise a PropertyChanged Event. When the event is raised some code should be executed.
My ValueChanged class:
public class ValueChange : INotifyPropertyChanged
{
public ValueChange()
{
_size = 1;
}
private int _size;
public int Size
{
get
{
return _size;
}
set
{
_size = value;
OnPropertyRaised("Size");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyRaised([CallerMemberName] string name = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
My event listeners:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
ValueChange test = new ValueChange();
test.Size = 10;
}
private void PropertyChanged(object sender, PropertyChangedEventArgs args)
{
switch (args.PropertyName)
{
case "Size":
// txtbox is just some textbox in my UI
txtbox.Text = "some text";
// This is merely a placeholder as I´d like to be able to execute any code in here
break;
}
}
}
There are a few issues with the code.
You are creating a new instance of the ValueChange class every time you click.
You are not subscribing to PropertyChanged event.
Although this will fix your code, is there a reason you are using PropertyChanged here instead of executing your code directly in the Button_Click event handler? PropertyChanged is usually used when binding, it is rarely used directly as you are doing here.
public partial class MainWindow : Window
{
ValueChange test = new ValueChange();
public MainWindow()
{
test.PropertyChanged += PropertyChanged;
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
test.Size = 10;
}
private void PropertyChanged(object sender, PropertyChangedEventArgs args)
{
switch (args.PropertyName)
{
case "Size":
// txtbox is just some textbox in my UI
txtbox.Text = "some text";
// This is merely a placeholder as I´d like to be able to execute any code in here
break;
}
}
}
I created a CustomButton, inherited from Button class. Then I created some event, like GotFocus, LostFocus, etc.
public sealed class CustomButton : Button
{
public CustomButton()
{
this.DefaultStyleKey = typeof(CustomButton);
}
protected override void OnApplyTemplate()
{
this.GotFocus += CustomButton_GotFocus;
this.LostFocus += CustomButton_LostFocus;
base.OnApplyTemplate();
}
private void CustomButton_LostFocus(object sender, RoutedEventArgs e)
{
//some common behavior code
}
private void CustomButton_GotFocus(object sender, RoutedEventArgs e)
{
//some common behavior code
}
}
Then I used this CustomButton in some pages. But in some specified pages, I don't want the CustomButton excute the GotFocus, LostFocus event.
So how to override these event in the specified pages?
I tried add GotFocus, LostFocus event in the specified pages, but it will finally run the common code behavior in CustomButton.
For the requirement, you could make a DependencyProperty to control the GotFocus or LostFocus event could be excuted or not.
For example:
public class CustomButton : Button
{
public CustomButton()
{
this.DefaultStyleKey = typeof(Button);
Current = this;
}
private static CustomButton Current;
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
}
public bool EnableDetected
{
get { return (bool)GetValue(EnableDetectedProperty); }
set { SetValue(EnableDetectedProperty, value); }
}
// Using a DependencyProperty as the backing store for EnableDetected. This enables animation, styling, binding, etc...
public static readonly DependencyProperty EnableDetectedProperty =
DependencyProperty.Register("EnableDetected", typeof(bool), typeof(CustomButton), new PropertyMetadata(0, new PropertyChangedCallback(OnValueChanged)));
private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if ((bool)e.NewValue == true)
{
Current.GettingFocus += Current_GettingFocus;
}
else
{
Current.GettingFocus -= Current_GettingFocus;
}
}
private static void Current_GettingFocus(UIElement sender, GettingFocusEventArgs args)
{
}
}
Usage
<local:CustomButton Content="Btn" EnableDetected="true" />
you can "take off" the events out of the CustomButton by iterating the page controls:
foreach(var cb in this.Controls.OfType<CustomButton>())
{
cb.GotFocus -= CustomButton.GotFocus;
cb.LostFocus -= CustomButton.LostFocus;
}
I believe this should work.
I'm just trying to get a grasp of MVVM pattern in WPF (currently without any framework).
Scenario:
I have a main window, I click a button "Start work" that is bound to some command in the viewmodel. Progress dialog should open with "Cancel" button, it should show on the center of the owner window(so I need to pass the owner), I press cancel and I invoke "CancelAsync" method on background worker.
The principle of MVVM is that the view model should never know anything about the view and in my case I'm violating this rule.
Code-behind (No MVVM) solution:
Main window part:
private void Button_Click(object sender, RoutedEventArgs e)
{
backgroundWorker.RunWorkerAsync();
progressWindow = new ProgressWindow(backgroundWorker);
progressWindow.Owner = this;
progressWindow.ShowDialog();
}
private void BackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
progressWindow.Close();
}
Progress window part:
private void btnCancel_Click(object sender, RoutedEventArgs e)
{
backgroundWorker.CancelAsync();
}
My attempt to convert this code to MVVM (this is wrong)
public class MainViewModel
{
public ICommand DoSomething { get; }
private BackgroundWorker backgroundWorker;
private PleaseWaitView pleaseWaitView;
public MainViewModel()
{
backgroundWorker = new BackgroundWorker() { WorkerSupportsCancellation = true };
backgroundWorker.DoWork += BackgroundWorker_DoWork;
backgroundWorker.RunWorkerCompleted += BackgroundWorker_RunWorkerCompleted;
var pleaseWaitViewModel = new PleaseWaitViewModel(backgroundWorker);
pleaseWaitView = new PleaseWaitView();
pleaseWaitView.Owner = Application.Current.MainWindow;
pleaseWaitView.DataContext = pleaseWaitViewModel;
DoSomething = new ActionCommand<object>(DoSomethingImpl);
}
private void BackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
pleaseWaitView.Close();
}
private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
// Some work
Thread.Sleep(5000);
}
private void DoSomethingImpl(object parameter)
{
pleaseWaitView.ShowDialog();
}
}
How to solve this? I did what I wanted in code-behind in a matter of 20 minutes, I wanted to try MVVM pattern and it takes me few hours to solve simple problem.
I was looking at some solutions with EventAggregator but that requires using a framework like Prism, Caliburn.Micro. So I get some kind of communication between VM and the View.
You can pass an interface to MainViewModel which contains the needed methods
interface IMainView
{
void Init(PleaseWaitViewModel viewModel);
void ShowDialog();
void Close();
}
public class MainViewModel
{
private IMainView _view;
public MainViewModel(IMainView view)
{
_view = view;
backgroundWorker = new BackgroundWorker() { WorkerSupportsCancellation = true };
backgroundWorker.DoWork += BackgroundWorker_DoWork;
backgroundWorker.RunWorkerCompleted +=
BackgroundWorker_RunWorkerCompleted;
var pleaseWaitViewModel = new PleaseWaitViewModel(backgroundWorker);
_view.Init(pleaseWaitViewModel);
}
private void BackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
_view.Close();
}
private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
// Some work
Thread.Sleep(5000);
}
private void DoSomethingImpl(object parameter)
{
_view.ShowDialog();
}
}
Messenger approach
public class PersonsViewModel
{
private RelayCommand _addPersonCommand = null;
public RelayCommand AddPersonCommand
{
get
{
return _addPersonCommand ?? (_addPersonCommand = new RelayCommand(
() =>
{
Action<Person> callback = (person) =>
{
_persons.Add(person);
RaisePropertyChanged("Persons");
};
Messenger.Default.Send<NotificationMessageAction<Person>>(new NotificationMessageAction<Person>(this, new Person(), "myNotification", callback), this);
}));
}
}
}
private PersonsViewModel _viewModel = null;
public PersonsView()
{
InitializeComponent();
DataContext = _viewModel = new PersonsViewModel();
Messenger.Default.Register<NotificationMessageAction<Person>>(this, _viewModel, message =>
{
if(message.Notification == "myNotification")
{
Person person = (Person)message.Target;
Action<Person> callback = message.Execute;
ModalView view = new ModalView(person);
if(true == view.ShowDialog())
{
callback.Invoke(view.Person);
}
}
});
}
Action property on view model approach
1) Add action property on the viewmodel
2) Wire it up in the view code behind
3) Invoke action it in the viewmodel logic where needed
using System;
using System.Windows;
using System.Windows.Input;
namespace WpfApp1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
// Wire up CancelAction in the View
var windowToClose = new Window();
var castedContext = (ViewModel) DataContext;
castedContext.CancelAction = () => windowToClose.Close();
}
}
public class ViewModel
{
private ICommand _doSomethingCommand;
public Action CancelAction { get; set; }
public ICommand DoSomethingCommand
{
get
{
if (_doSomethingCommand != null)
return _doSomethingCommand;
_doSomethingCommand = new MyCommandImplementation(() =>
{
// Perform Logic
// If need to cancel - invoke cancel action
CancelAction.Invoke();
});
return _doSomethingCommand;
}
}
}
// Stubbed out for the sake of complete code
public class MyCommandImplementation : ICommand
{
public MyCommandImplementation(Action action)
{
throw new NotImplementedException();
}
public bool CanExecute(object parameter)
{
throw new NotImplementedException();
}
public void Execute(object parameter)
{
throw new NotImplementedException();
}
public event EventHandler CanExecuteChanged;
}
}
I want to disable button(or other control) when user can't raise event. What is the best way to do this? View handles that or presenter should pass value by property in view and then view will update control's state.
For example if previous query is not finished user shouldn't start new.
Option 1:
interface IView
{
event EventHandler Event;
}
class View : IView
{
private readonly Button _button;
public event EventHandler Event;
public void button_Click(object sender, EventArgs e)
{
_button.Enabled = false;
if(Event != null)
{
Event(this, EventArgs.Empty);
}
_button.Enabled = true;
}
}
class Presenter
{
public void View_Event(object sender, EventArgs e)
{
// code...
}
}
Option 2:
interface IView
{
event EventHandler Event;
bool CanRaiseEvent { set; }
}
class View : IView
{
private readonly Button _button;
public event EventHandler Event;
public bool CanRaiseEvent
{
set
{
_button.Enabled = value;
}
}
public void button_Click(object sender, EventArgs e)
{
if (Event != null)
{
Event(this, EventArgs.Empty);
}
}
}
class Presenter
{
private readonly IView _view;
public void View_Event(object sender, EventArgs e)
{
_view.CanRaiseEvent = false;
// code...
_view.CanRaiseEvent = true;
}
}
I know that i should check in presenter query's status before executing next query but I want to inform view that user shouldn't even try.
Two 'litmus' tests I use for MVP design are: 1) Is the logic testable? and 2) Could I replace the concrete view and the application still work?
From this perspective, option 2 looks the more attractive.
Is there a way to give a User Control custom events, and invoke the event on a event within the user control. (I'm not sure if invoke is the correct term)
public partial class Sample: UserControl
{
public Sample()
{
InitializeComponent();
}
private void TextBox_Validated(object sender, EventArgs e)
{
// invoke UserControl event here
}
}
And the MainForm:
public partial class MainForm : Form
{
private Sample sampleUserControl = new Sample();
public MainForm()
{
this.InitializeComponent();
sampleUserControl.Click += new EventHandler(this.CustomEvent_Handler);
}
private void CustomEvent_Handler(object sender, EventArgs e)
{
// do stuff
}
}
Aside from the example that Steve posted, there is also syntax available which can simply pass the event through. It is similar to creating a property:
class MyUserControl : UserControl
{
public event EventHandler TextBoxValidated
{
add { textBox1.Validated += value; }
remove { textBox1.Validated -= value; }
}
}
I believe what you want is something like this:
public partial class Sample: UserControl
{
public event EventHandler TextboxValidated;
public Sample()
{
InitializeComponent();
}
private void TextBox_Validated(object sender, EventArgs e)
{
// invoke UserControl event here
if (this.TextboxValidated != null) this.TextboxValidated(sender, e);
}
}
And then on your form:
public partial class MainForm : Form
{
private Sample sampleUserControl = new Sample();
public MainForm()
{
this.InitializeComponent();
sampleUserControl.TextboxValidated += new EventHandler(this.CustomEvent_Handler);
}
private void CustomEvent_Handler(object sender, EventArgs e)
{
// do stuff
}
}