I have a simple WPF Control with a button on it that uses caliburn micro event wiring (caliburn cheat sheet):
<UserControl xmlns:cal="http://www.caliburnproject.org">
<Button Name="Save" Content="Save"
cal:Message.Attach="[Event MouseEnter] = [Action DoSmthg]" />
</UserControl>
The user control is integrated to the main view as
<ContentControl cal:View.Model="{Binding MyViewModel}" />
In the view model I have the corresponding method:
public void DoSmthg()
When I start my app, the method is not called, so I initial considered that I made a mistake with the wiring. But when I modify the XAML file while debugging (e.g. adding a space), everything works as expected. SO it looks to me, that while startup, the event is overridden by something else, and when I update the XAML the caliburn event is updated again. Since I removed everything that could disturb in my code I have no idea what the reason could be. Any ideas, how I can find the issue?
The issue is independent of the event type. Also happens when I add parameters. And no errors or warnings in output.
Remark: When I add a breakpoint in the OnInitialized(EventArgs e) method of the code behind and execute Message.GetAttach(Save) I can see that the event is set up correctly.
Related
in my window I want to use this code
<i:Interaction.Triggers>
<i:EventTrigger EventName="SourceInitialized">
<command:EventToCommand Command="{x:Static wpf:Window.InitializeWindowProcessHookCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
to hook the SourceInitialized event to a command on my so-called Window class.
I am using MvvMLight EventToCommand and if it works perfectly if I use the Loaded event instead of the SourceInitialized, so we can assume that the command and further logic is working.
Additionally, using the event with code behind works, but I am looking for a solution using EventTriggers (if possible).
When looking up a possible solution, I stumbled across a topic on MSDN, which is about this exact topic, and the OP points states that he successfully bound to the Loaded event, but cannot get binding to the SourceInitialized to work
[and I] want to write a similar one for windows source initialized event, but find that Window.SourceInitializedEvent is not exposed
Is there any possible solution to that?
Many thanks in regard
The SourceInitialized event fires before your trigger has a chance to invoke the command so this won't work.
Also, it doesn't make much sense to fire the command using an EventTrigger that is defined in the XAML markup just for the sake of not having to do it from the code-behind of the same view. MVVM is not about eliminating view-related code from the views and it doesn't break the pattern to invoke the command from the code-behind of the same view as your XAML markup is defined in.
So invoke the command from an event handler in the code-behind or subscribe to another event.
As you have already noticed, there is no way to handle the SourceInitialized event of a window using an EventTrigger that is defined in the XAML markup of the same window.
I have a UI with a few controls.
Initially when form loads, search button will be disabled, once all
search criteria are given, search button will be enabled
automatically.
On click on search button, I want to call the method
using MVVM pattern and bind the result in grid
XAML:
<Button Name="btnGetDetails" Content="Get Details" Grid.Row="2" Command="{Binding SearchCommand}"/>
What code is required in model, view model and XAML?
Command will be executed only when the button will be clicked on. If you need to do something to the button then you should do it to the Window that contains the button (assuming your button is in a Window). Now if you want to stick with the MVVM pattern then you should not use the Window.OnLoaded, because that would put code in your code behind. One option is to use System.Windows.Interactivity, which you have download seperately. Here is what it will look like:
<Window x:Class="..."
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="clr
-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding ...}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Window>
As for what your Model, View and ViewModel should be, I think you should check out some tutorials on the web. There are some very good explanations on how to implement the MVVM pattern. I found this youtube video pretty informative myself:
http://www.youtube.com/watch?v=EpGvqVtSYjs
So some MVVM basics here. You're on the right track, just missing a step. Your Command implementation in your view model should (potentially) accept two inputs: An Action representing the executing code, and a Predicate that returns true/false on whether you can execute the code in the Action block. So, in your view model, define your command along the lines of (note: this is a sample from one of my projects):
this.executeCommand = new RelayCommand(this.OnExecuteClicked, this.OnCanExecuteChanged);
The OnCanExecuteChanged method will return a bool based on whatever criteria you set up. So, if you want the submit button enabled when property A and property B have been properly set up, then return true, else return false. The internal workings of your command implementation will take care of the rest. Do a search for a RelayCommand implementation (if you don't have it already) or a DelegateCommand for further samples.
View model is loading data asynchronously using background worker thread in model. All properties in the model and view model raise the property changed events, and all properties are being updated in the view as expected, except 2 buttons whose IsEnabled state depends on the outcome of some properties that are loaded.
The frustrating part is that as soon as I focus on any part of the view, or set a breakpoint after the properties are updated (create a delay), then the buttons IsEnabled state is updated as expected. So it appears to be a timing issue.
Any clues as to how to the best way to solve this? I'm using mvvm-light framework, but that shouldn't matter.
I've tried binding IsEnabled to the button instead of just relying on the Command property, but that made no difference. I've confirmed via logging that view model properties are set and the PropertyChanged event is being raised for the properties associated with the buttons.
Considering sending a message using mvvm-light messenger from the view model to the view on the async completed event and then somehow? triggering a view refresh, but that seems like a kludge.
Update
Thanks to blindmeis' answer, I tested the button behaviour without the Command binding set, i.e. just binding IsEnabled property, and it works as expected!
<Button
Grid.Column="2" Content="{Binding LoadProjectsLabel}"
VerticalAlignment="Top" HorizontalAlignment="Right"
IsEnabled="{Binding CanLoadProjects}" />
Obviously it's not great because I can no longer execute the command :) but as soon as I add the command back, it stops behaving:
<Button
Grid.Column="2" Content="{Binding LoadProjectsLabel}"
VerticalAlignment="Top" HorizontalAlignment="Right"
Command="{Binding LoadProjectsCommand}" />
Leaving IsEnabled binding doesn't solve the problem, but that seems like a good clue.
The view model command code:
public ICommand LoadProjectsCommand
{
get
{
if (_loadProjectsCommand == null)
{
_loadProjectsCommand = new RelayCommand(loadProjects, () => CanLoadProjects);
}
return _loadProjectsCommand;
}
}
Workaround
Wire up the Click event and avoid Command. Would be nice to solve it from the view model, but this works:
<Button
Grid.Column="2" Content="{Binding LoadProjectsLabel}"
VerticalAlignment="Top" HorizontalAlignment="Right"
IsEnabled="{Binding CanLoadProjects}"
Click="loadProjects_Click"/>
Code behind:
void loadProjects_Click(object sender, RoutedEventArgs e)
{
SettingsViewModel vm = (SettingsViewModel)DataContext;
vm.LoadProjectsCommand.Execute(null);
}
Answer from other thread:
When your BackgroundWorker completes, call CommandManager.InvalidateRequerySuggested();
By default, Commands are only requeried occasionally by WPF. Otherwise, there would be a huge amount of overhead in constantly calling "CanExecute" on every ICommand implementation. Calling the above method forces the CommandManager to update immediately.
This will force the Commands to re-enable/disable appropriately.
EDIT:
i use a simpler but not so beautiful workaround. i simply call OnPropertyChanged("MyICommand") for my commands in my BackgroundWorker Completed Event.
EDIT:
here is another nice solution.
You should bind command parameter property to any updatable property on viewmodel and can execute must use this command parameter to enable button. If command parameter's target is updated, binding will enable/disable based on return value of can excute.
I have this problem in a bigger Project...... so I set up a 'Testpoject' as Proof of Concept:
New Silverlight-Application
Add Listbox
Fill listbox with a few Checkboxes
Register listBox1_MouseLeftButtonDown
register listBox1_MouseRightButtonDown
You will see, that the listBox1_MouseLeftButtonDown won't fire under any circumstances....
listBox1_MouseRightButtonDown however fires just fine.
I tried using a custom Class deriving from ListBox and overriding, assuming something in the ListBox Class was setting e.Handled = false, but this did not change the behaviour, either.
Any Ideas on why this happens and how to fix?
(This problem also stops the 'parent'-control from receiving the Click-Event... so the Event-passing is broke)
:edit:
I fixed my problem with a workaround... so an answer is not required anymore. Just if somebody feels like figuring out why this is happening for the sake of it ;)
This seems to answer your question. To quote:
That's because ListBoxItem internally handles this event as well as the MouseLeftButtonDown event (halting the bubbling) to implement item selection.
The solution is to add the event handler in the code-behind file. From the article:
Although setting the RoutedEventArgs parameter's Handled property to true in a routed event handler appears to stop the tunneling or bubbling, individual handlers further up or down the tree can opt to receive the events anyway! This can only be done from procedural code, using an overload of AddHandler that adds a Boolean handledEventsToo parameter.
See the caveat at the end though.
This is by design. If you check the framework code, you'll see that the ListBoxItem sets the Handled property to true.
I had this same exact problem, so in my ListBoxItem.ItemTemplate, I added the event handler in my content.
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" MouseLeftButtonDown="StackPanel_MouseLeftButtonDown">
... other controls ...
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
I've scoured the Internet but my search-foo must not work if it's out there cause I just can't find the proper search terms to get me the answer I am looking for. So I turn to the experts here as a last resort to point me in the right direction.
What I'm trying to do is create a composite control of a text box and a list box but I want to allow the control consumer to decide what to do when say the checkbox is checked/unchecked and I just can't figure it out... Maybe i'm going about it all wrong.
What I've done thus far is to:
create a custom control that extends ListBox
expose a custom DP named "Text" (for the Text box but it's not the important part)
craft the generic.xaml so that the list items have a default ItemTemplate/DataTemplate
inside the DataTemplate I'm trying to set the "Checked" or "Unchecked" events
expose 'wrapper' events as DPs in the custom control that would get 'set' via the template when instatiated
As soon as I try something like the following (inside generic.xaml):
<DataTemplate>
<...>
<CheckBox Checked="{TemplateBinding MyCheckedDP}"/>
<...>
</DataTemplate>
I get runtime exceptions, the designer - vs2010 - pukes out a LONG list of errors that are all very similar and nothing I do can make it work.
I went so far as to try using the VisualTreeHelper but no magic combination I could find would work nor would it allow me to traverse the tree because when the OnApplyTemplate method fires the listbox items don't exist yet and aren't in the tree.
So hopefully this all makes sense but if not please let me know and I'll edit the post for clarifications.
Thanks to all for any pointers or thoughts... Like I said maybe I'm heading about it in the wrong way from the start...
EDIT (Request for xaml)
generic.xaml datatemplate:
<DataTemplate >
<StackPanel Orientation="Horizontal" >
<local:FilterCheckbox x:Name="chk">
<TextBlock Text="{Binding Path=Display}" />
</local:FilterCheckbox>
</StackPanel>
</DataTemplate>
usercontrol.xaml (invocation of custom control)
<local:MyControl FancyName="This is the fancy name"
ItemChecked="DoThisWhenACheckboxIsChecked" <-- this is where the consumer "ties" to the checkbox events
ItemsSource="{Binding Source={StaticResource someDataSource}}"
/>
Assuiming that you want to fire the event from your Custom Control, while clicking on the checkBox , and handling it in your implementing class,
the following should work.
First of all try to remove the template binding with event, though we expect it may work as it works templateBinding DependencyProperty
So your XAML will look something as below:
<DataTemplate>
<...>
<CheckBox x:Name="myCheckBox" />
<...>
</DataTemplate>
Now You have to access your checkBox from generic.cs as below:
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
CheckBox myCheckBox= GetTemplateChild("myCheckBox") as CheckBox;
}
Now You just need to add a event to your Control.
public event EventHandler checkBoxChecked;
Now this are typically implemented like this:-
protected virtual void onCheckBoxChecked(EventArgs e)
{
var handler = checkBoxChecked;
if (handler != null)
handler(this, e);
}
Now in your existing checked events:-
myCheckBox.Checked+= (obj, Args) =>
{
onCheckBoxChecked(EventArgs.Empty);
}
Now in your consuming code or Implementing Application you can do this sort of thing(Assuming your CustomControl Name is myCustomControl):-
myCustomControl.checkBoxChecked+=(s, args) => { /* Your Code Here*/ };
Hope this is what you were trying to acomlish. If need anything esle , letz discuss here.
Update
Well there is detailed discussion about onApplyTemplate and loading the controls and accessing the events outside that class:
Have a look:
How to Access a Button present inside a Custom Control, from the implementing page?
Well I dislike answering my own question but the answer above doesn't work for me nor does the link referenced do what i'm looking for.
I'm posting this only answer so that i can maintain the 100% answer rate...