I'm facing some problems binding a CommandParameter to its own Command in an application built using Prism 2.2 as MVVM . Let me introduce what it's happening.
I've got a customized listbox with a property named NumPageElements, and a couple of buttons to scroll through the list who needs that property. A simplified xaml of what I need (and works) in wpf is:
<Button x:Name="PageDownButton" Command="{Binding PageDownCommand}" CommandParameter="{Binding ElementName=ItemsListBox, Path=NumPageElements}" />
<Custom:MyOwnListBox x:Name="ItemsListBox" x:NumPageElements="{Binding ElementsPerPage, Mode=OneWayToSource}" >
. . .
</Custom:MyOwnListBox>
To have the same behaviour in Silverlight I wrote this xaml:
<Button Name="PageDownButton" Command="{Binding PageDownCommand}" CommandParameter="{Binding ElementName=ItemsListBox, Path=NumPageElements}" />
<Custom:MyOwnListBox Name="ItemsListBox" NumPageElements="{Binding Path=ElementsPerPage, Mode=TwoWay, UpdateSourceTrigger=Explicit}" >
. . .
</Custom:MyOwnListBox>
PageDownButton is an IApplicationCommand, ElementsPerPage is a property exposed by the presenter.
Now, the first time I open this view the buttons made in that way look enabled but they aren't 'clickable'. If I switch to a different view and I go back to the view with those button, they finally catch the correct behavior. It looks like it doesn't initialize correctly the first time the condition of the command (infact they should be disabled until I insert an item in the listbox), as if the parameter given via the CommandParameter property isn't initiliazed correctly. But I can't understand why switching between the views make it works.
I suspected I should force the UpdateSource of the bindings (I did it for ItemsListBox.NumPageElements and for PageDownButton.CommandParameter) after the view has been loaded, but doing it in the code behind was not of any help.
What I am doing wrong?
Thanks for any reply,
Mat.
Related
Good Morning!
I have a WPF application that will display a number of different file types based on command line args it receives. It works fine, but I want to go back and refactor it. I have only been a developer for a few years and would like to master MVVM.
I am using an MVVM design package called Stylet. In my PDF view I am using a Telerik RadPdfViewer control to which Telerik has all this binding stuff built in for you. For example, I am binding the right click context menu with the commands "select all" and "copy" using their pre configured command bindings.
I would like to bind the "Document Source" property TO MY viewmodel so I can pass in the paths of documents I want to load. However, the DataContext of the control is bound to Telerik's CommandDescriptors preventing the binding to my viewmodel.
<telerik:RadPdfViewer x:Name="radPdfViewer" Grid.Row="1"
DataContext="{Binding CommandDescriptors, ElementName=radPdfViewer}"
DocumentSource="{Binding PDFDoc}"
telerik:RadPdfViewerAttachedComponents.RegisterFindDialog="True"
HorizontalAlignment="Stretch" Margin="0,0,0,0" VerticalAlignment="Stretch"
telerik:StyleManager.Theme="Office_Black" Grid.ColumnSpan="2">
<telerik:RadContextMenu.ContextMenu>
<telerik:RadContextMenu>
<telerik:RadMenuItem Header="Select All"
Command="{Binding SelectAllCommandDescriptor.Command}" />
<telerik:RadMenuItem Header="Copy"
Command="{Binding CopyCommandDescriptor.Command}" />
</telerik:RadContextMenu>
</telerik:RadContextMenu.ContextMenu>
</telerik:RadPdfViewer>
public class PDFViewModel
{
private string _pdfDoc;
public string PDFDoc
{
get
{
return _pdfDoc;
}
set
{
_pdfDoc = value;
}
}
public PDFViewModel()
{
PDFDoc = #"t:\share\large.pdf";
}
}
I see two choices
I break Telerik's prebuilt command bindings and figure out how to bring the select all and copy functions to my viewmodel.
Stylet has an s:Action function where I can call a method where I can load the document into the RadPdfViewer control using C#. I would need to somehow get control of the gui control in the method of my viewmodel and I am not sure how to do that.
Is there a better way? A little nudge in the right direction would be greatly appreciated.
Jason Tyler's reply got me going in the right direction. Thank you!
So because I am using a ViewModel first pattern, I did not need to specify the DataContext of the user control like I thought...Its already set.
However, his suggestion of binding using the relative source and researching on how to do this (I have never used RelativeSource before..I am kinda new to this stuff) I came across this Stack post
How do I use WPF bindings with RelativeSource?
A Jeff Knight Posted a diagram of how ancestor binding works.
Using that, I was able to figure out the syntax and my document came right up and I can still use the right click context menu items that are bound to Telerik. So now my Xaml looks like this note how the Document source binding has changed.
<telerik:RadPdfViewer x:Name="radPdfViewer" Grid.Row="1"
DataContext="{Binding CommandDescriptors, ElementName=radPdfViewer}"
DocumentSource="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=DataContext.PDFDoc}"
telerik:RadPdfViewerAttachedComponents.RegisterFindDialog="True"
HorizontalAlignment="Stretch" Margin="0,0,0,0" VerticalAlignment="Stretch"
telerik:StyleManager.Theme="Office_Black" Grid.ColumnSpan="2">
<telerik:RadContextMenu.ContextMenu>
<telerik:RadContextMenu>
<telerik:RadMenuItem Header="Select All"
Command="{Binding SelectAllCommandDescriptor.Command}" />
<telerik:RadMenuItem Header="Copy"
Command="{Binding CopyCommandDescriptor.Command}" />
</telerik:RadContextMenu>
</telerik:RadContextMenu.ContextMenu>
</telerik:RadPdfViewer>
I have a page that can contain 4 different user controls, but only one of them should be visible at a time. I would like to do something like this:
<regControls:MyUserControl1 Visibility="{Binding Ctrl1, Mode=TwoWay, Converter={StaticResource BoolToVisibilityConverter}" />
<regControls:MyUserControl2 Visibility="{Binding Ctrl2, Mode=TwoWay, Converter={StaticResource BoolToVisibilityConverter}" />
<regControls:MyUserControl3 Visibility="{Binding Ctrl3, Mode=TwoWay, Converter={StaticResource BoolToVisibilityConverter}" />
<regControls:MyUserControl4 Visibility="{Binding Ctrl4, Mode=TwoWay, Converter={StaticResource BoolToVisibilityConverter}" />
But when the component is initialised the converter isn't triggered at all. Even though I bind the property and raise execute change.
I would prefer not to initialise the different controls in the code behind. I'm using WP8 Silverlight (NOT WP 8.1). What am I missing?
EDIT:
I'm beginning to understand the problem. But I don't know how to solve it. We're using GWT so I get a call from GWT layer that tells me what to show:
public void ShowMyUserControl1()
{
SimpleIoc.Default.Register<MyPageViewModel>();
SimpleIoc.Default.Register<MyUserControl1ViewModel>();
SimpleIoc.Default.Register<MyUserControl2ViewModel>();
SimpleIoc.Default.Register<MyUserControl3ViewModel>();
SimpleIoc.Default.Register<MyUserControl4ViewModel>();
navigationService.NavigateTo(typeof(MyPageViewModel).FullName);
var viewModel = SimpleIoc.Default.GetInstance<MyPageViewModel>();
viewModel.DisplayMyUserControl1();
}
public void ShowMyUserControl2()
{
....
}
The problem here is that the view model is called and properties triggered before the components are initialised. The problem here is also that I don't know which method is called from GWT. It could be ShowMyUserControl1 or ShowMyUserControl2 (or method requesting control 3 or 4 to be shown. So somehow I need to tell this and at the same time pass parameters to the correct view model with arguments coming from GWT. Messenger (MVVMLight) doesn't work here. Because the message is sent before any control has been initialised and registered to listen to these messages. The user can have left the app and turn off the phone and then come back again, so I need to expect that the view models doesn't exist and needs to be registered.
Is DataContext (ViewModel) support INotifyPropertyChange?
Any binding errors in console output in VS?
Create breakpoint on property. Does it hint?
I'm having the weirdest problem and it's driving me insane.
I've created a WPF MVVM program and everything was working alright, however, now when I open the program and click on a button, I receive a System.NullReferenceException. I put a breakpoint where the error occurs and the button isn't instantiated, however, the form shows just fine and the buttons are clickable. In fact, none of the buttons instantiate (every button on my form gives the same error, and when I set a breakpoint after InitializeComponent(), none of the buttons show up under this - all the other components show up.
Here is sample code for the button:
MainWindow.xaml
<Button Content="A"
Command="{Binding KeyButtonClickCommand}"
Style="{StaticResource keyButtonStyle}" />
The method throwing an error is in my ViewModel, the button is bound to a command:
private void keyButton_Click(object sender)
{
Button btn = (Button)sender;
string tempKey = "";
tempKey = btn.Content.ToString();
this.Key = tempKey;
}
Breakpoint after InitializeComponent()
Breakpoint after error
Like I said, was working just fine earlier, now just went on the fritz.
What worries me most is that maybe I've done something that I shouldn't have and it might affect future projects. I'd just like to double-check whether it's that, or just a freak occurrence.
Thanks.
If I am understanding your question correctly you are getting the argument for your keyButton_Click method as null.
This is most likely because you are not passing in a CommandParameter to your command. If you want to pass the button itself into the command, try the following XAML.
<Button Content="A"
Command="{Binding KeyButtonClickCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource Self}}"
Style="{StaticResource keyButtonStyle}" />
I do want to note the whole point of MVVM is to not interact with the actual UI layer directly in your ViewModel and when you pass your button to your ViewModel like this you are doing exactly that.
Edit : As noted in the comments if you want to pass "A" as argument to your command you should just set that as your CommandParameter.
<Button Content="A"
Command="{Binding KeyButtonClickCommand}"
CommandParameter="A"
Style="{StaticResource keyButtonStyle}" />
I have a ViewModel which that is defined in my application resources, this ViewModel has a command called RunCommand
and in my MainWindow i am trying to bind that command to a button without setting the datacontext so i tried
<Button Command="{Binding Source={StaticResource ViewModel.RunCommand}}"/>
it showed an exception, however when i do the following things work fine
<Button DataContext="{Binding Source={StaticResource ViewModel}}" Command="{Binding RunCommand}"/>
what is wrong with the first part, and do i have to set the datacontext for such a simple task?
You are certainly not forced to change/set the DataContext just so you can bind a simple property.
Here's what you want
<Button Command="{Binding RunCommand, Source={StaticResource ViewModel}}"/>
Setting the datacontext is a good thing to do ... it takes away the voodoo of what object you're talking to. I believe all MVVM frameworks help you out with Locators, and when not using them, you can use your code behind.
It's just the way the language works.
I am currently writing a Windows 8 application. I am trying to call a method in my ViewModel. I want this method to be called when an item is double clicked. I have defined the following DataTemplate in my XAML to do this:
<DataTemplate x:Key="ItemTemplate">
<StackPanel Orientation="Horizontal">
<Image Width="185" Height="185" Stretch="Fill" Source="{Binding Path=Image}" DoubleTapped="{Binding Path=MethodIWishToBindTo}" IsDoubleTapEnabled="True" />
</StackPanel>
</DataTemplate>
The problem, of course, is the error message for my binding to MethodIWishToBindTo:
Invalid value for 'DoubleTapped'. Event values must be text
What is the best way for me to get around this ? I could call the method in the code-behind, however the method uses a property in my ViewModel, "SelectedItemInList", which I don't believe can be accessed from the code behind.
Can anyone offer me some advice for this problem ?
Thanks a lot.
You could use Interactivity and a custom behavior to trigger the event. Here's a post that topically covers an example:
MVVM-Light EventToCommand Behavior for CheckBox Checked/Unchecked in Silverlight
MVVM-Light definitely makes this easier, but it's possible without as well.
Here's an example of without: http://blog.roboblob.com/2010/01/26/binding-ui-events-from-view-to-commands-in-viewmodel-in-silverlight-4/