Relative new to MVVM - I realize this is simple question but can't seem to find the answer.
I have 4 grouped radio buttons that when one is selected will show its associated options. I assume these 4 radio buttons should be linked to the same viewmodel command which in this case is called UpdateIndex.
How do I determine which of the radio buttons is calling the UpdateIndex so that I can take appropriate action and show the appropriate options?
Note that my UpdateIndex and UpdateIndexExecute does get called correctly from my radio button command binding, I just don't know how to determine which radio button is calling it. I assume it has to do with CommandParameter - but not sure how to access it from the viewmodel.
An example of my radio button:
<RadioButton
Content="Option 1"
GroupName="GroupHeader"
Command="{Binding UpdateIndex}" />
Code snippet of my command being called from the radio button when clicked...
void UpdateIndexExecute()
{
}
bool CanUpdateIndex()
{
return true;
}
public ICommand UpdateIndex
{
get
{
return new RelayCommand(UpdateTabIndexExecute, CanUpdateTabIndex);
}
}
In a true MVVM implementation, you won't know which RadioButton executed the command, because the ViewModel should not have any information about the View. User Controls fall squarely in the category of "things that only exist within the View, not the ViewModel." You should instead pass something meaningful to the ViewModel.
You are correct there are ways to pass information into an ICommand using the "CommandParameter" of a Command Binding. For that, you would use the "generic" form of the RelayCommand (RelayCommand) class, where "T" represents the type of object you are passing as a parameter.
If you're just trying to pass an index as a parameter, I imagine it'll look something like this:
<!-- We are passing index "1" as a parameter -->
<RadioButton Content="Option 1" GroupName="GroupHeader"
Command="{Binding UpdateIndex, CommandParameter=1}"/>
Then in your ViewModel:
void UpdateIndexExecute(int index)
{
}
bool CanUpdateIndex(int index)
{
return true;
}
public ICommand UpdateIndex
{
get
{
return new RelayCommand<int>(UpdateTabIndexExecute, CanUpdateTabIndex);
}
}
Instead of binding the command, you can bind the content, use the INotifyPropertyChanged interface to handle changes made by the control.
Related
I am trying to execute a bound command from my code behind utilizing the UiElement. button.Command.Execute(button.CommandParameter)
However, at this point the Command property of the button is null. simultaneously when I check the command in my View Model the property is set. The only diagnosis I can come up with is that until the window is actually visible the command is not bound to the command property of the button. I feel like may I'm missing a step somewhere or my implementation is not sound. below is some snipits of the code, please let me know if you need more.
Window constructor:
public PlottingViewModel ViewModel { get; set; }
public PlottingGUI()
{
InitializeComponent();
DataContext = (ViewModel = new PlottingViewModel());
_setDefaultSelections();
}
IList<RadioButton> buttons;
Setting default selections:
private void _setDefaultSelections()
{
buttons = new List<RadioButton>();
_getRadioButtons(this);
foreach (var setting in ViewModel.Settings.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public))
{
var settingValue = setting.GetValue(ViewModel.Settings);
var button = buttons.FirstOrDefault(btn => btn.Content.Equals(settingValue)
|| ((string)btn.CommandParameter).Equals(settingValue));
if (button == null)
continue;
button.IsChecked = true;
// NullReference here
// button.Command.Execute(button.CommandParameter);
}
}
one of the RadioButtons XAML:
<RadioButton Content="None"
Grid.Row="0"
Command="{Binding StampedCommand}"
CommandParameter="None"
Foreground="WhiteSmoke"/>
I feel, the only way i may be able to successfully complete this task is to execute the command directly from my viewmodel. (Which i don't want to do)
Thanks for reading..
To sum up comments at the point when you're calling _setDefaultSelections() bindings have not been updated yet, hence Command is still null, so you have to wait until everything is loaded. You can call _setDefaultSelections during Loaded event
Occurs when the element is laid out, rendered, and ready for interaction.
I had a problem with TreeView-Binding and ContextMenu here: Selected TreeViewItem is null
Now I'm having this problem: I have the ContextMenu
<TreeView.ContextMenu>
<ContextMenu x:Name="MyContext" ItemsSource="{Binding OCContext}" DisplayMemberPath="Text"/>
</TreeView.ContextMenu>
(The image shows how my ContextMenu looks like, don't mind about the tabItem...).
As you can see, it's just the ContetMenu, no MenuItem! If the user clicks on Close, I want to do something in my ViewModel (raise a Command?). I'd also like to know which button/Menu he clicked. The amount of Menus is dynamically, since it's ItemsSource is being binded.
This is my ViewModel:
private ObservableCollection<T_Antwort> _occontext;
public ObservableCollection<T_Antwort> OCContext
{
get
{
if (_occontext == null)
_occontext = new ObservableCollection<T_Antwort>();
return _occontext;
}
set
{
_occontext = value;
RaisePropertyChanged(() => OCContext);
}
}
So all I want to do is to bind the ContextMenu (The "items" Close and CloseOtherThankThis) to my ViewModel, so when the user clicks on one of them, I want to access them in my ViewModel. This means I don't want to bind them one by one, I want somehow to get an event (ContextMenuItemClicked (?)) being called and use this in my ViewModel.
Btw. using MenuItem under ContextMenu will create another "Menu folder", so it would be
" " -> Close
" " -> CloseOtherThankThis
And I don't want it to look like this.
Edit: I'm currently getting the item like this:
private void MyContext_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
System.Windows.Controls.Primitives.MenuBase s = sender as System.Windows.Controls.Primitives.MenuBase;
ItemCollection ic = s.Items;
T_Antwort SelectedItem = (T_Antwort)ic.CurrentItem;
}
Is there any possibility to get the selected item with binding?
Don't know if you have tried it, but there's a PlacementTarget for context menu, which gives you the object that contains the context menu.
In one project I had, I made something like this:
<MenuItem ... Visibility="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type ContextMenu}},Path=PlacementTarget.SelectedItem
In my SL4 app, I'm trying to trigger a Command from a button. The Command code is standard stuff that I have used without issue elsewhere, but I cannot get the Command to be called when I click the button.
This was driving me mental, so I eventually created a test page that had nothing on it but a button. The data context of the page is set to my ViewModel, and the ViewModel has an ICommand property on it. The DataContext is working as I can bind a textbox to a string property in the ViewModel. It's so basic, I can include all the relevant stuff here:
From the XAML:
xmlns:models="clr-namespace:x3.ViewModels"
...
<UserControl.DataContext>
<models:TestViewModel/>
</UserControl.DataContext>
<Button x:Name="TestButton" Command="{Binding TestCommand}" Content="AAAAGHH" />
From the ViewModel:
public class TestViewModel:INotifyPropertyChanged
{
ICommand _testCommand;
public ICommand TestCommand
{
get
{
_testCommand = new DelegateCommand(
commandParameter =>
{
var testButton = commandParameter as Button;
},
(commandParameter) => {return true;});
return _testCommand;
}
}
}
The DelegateCommand is part of Telerik.Windows.Controls. If I put a breakpoint at
_testCommand = new DelegateCommand
it gets hit when the page loads, but after that, I can click the button until my mouse wears out, and the command is never called.
For the sake of my mental health, I'd appreciate any help on offer.
Thanks
Mick
The get accessor for TestCommand is only called once - when the binding engine binds the Command of the Button to the TestCommand property. Putting your breakpoint on the first line of the get, it should be expected that it only gets hit once.
What you need to do is put your breakpoint on the code that executes when your command gets run. In your original example, this means break inside the delegate - i.e. on return true.
Edit: you can force the debugger to break in code as well using System.Diagnostics.Debugger.Break():
ICommand _testCommand;
public ICommand TestCommand
{
get
{
_testCommand = new DelegateCommand(
commandParameter =>
{
var testButton = commandParameter as Button;
},
(commandParameter) =>
{
System.Diagnostics.Debugger.Break(); // Force debugger to break
return true;
}
);
return _testCommand;
}
}
In order to isolate the problem, try using your own simple ICommand implementation, instead of Telerik's DelegateCommand.
If the problem still happens, you'll know it's somewhere around your XAML / data binding, and then I'd suggest you post more complete parts of your code so that someone here may help you.
If the problem disappears, you'll know it's something in Telerik DelegateCommand that decides not to call your lambda.
If you can post a more complete reproduction of the problem, it may also help. Because you posted only a minimal part of your code, and maybe the cause of the problem is missing from here...
I know I am missing something here and I could use a pointer. Within a project I have an expander control when this control is clicked it makes a RIA call to a POCO within my project to retreive a second set of data. I am using the SimpleMVVM toolkit here so please let me know if I need to expand on any additional areas.
Within the xaml the expander is laid out as
<toolkit:Expander Header="Name" Style="{StaticResource DetailExpanderSytle}" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="Expanded">
<ei:CallMethodAction
TargetObject="{Binding Source={StaticResource vm}}"
MethodName="showWarrantNameDetail"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<StackPanel Orientation="Vertical">
<sdk:DataGrid AutoGenerateColumns="true" ItemsSource="{Binding NameResult}" AlternatingRowBackground="Gainsboro" HorizontalAlignment="Stretch" MaxHeight="200">
</sdk:DataGrid>
<local:NameContainer DataContext="{Binding}" />
</StackPanel>
</toolkit:Expander>
I am using the expression Dll coupled with Simple MVVM to get at the methods in the view model vs commands.
Within the view model I have the following code
public void showWarrantNameDetail()
{
//set flags
IsBusy = true;
CanDo = false;
EntityQuery<WarrantNameDataView> query = App.cdContext.GetWarrantNameDataViewsQuery().Where(a => a.PrimaryObjectId == Convert.ToInt32(RecID));
Action<LoadOperation<WarrantNameDataView>> completeProcessing = delegate(LoadOperation<WarrantNameDataView> loadOp)
{
if (!loadOp.HasError)
{
processWarrantNames(loadOp.Entities);
}
else
{
Exception error = loadOp.Error;
}
};
LoadOperation<WarrantNameDataView> loadOperation = App.cdContext.Load(query, completeProcessing, false);
}
private void processWarrantNames(IEnumerable<WarrantNameDataView> entities)
{
ObservableCollection<WarrantNameDataView> NameResult = new ObservableCollection<WarrantNameDataView>(entities);
//we're done
IsBusy = false;
CanDo = true;
}
When I set a break on the processWarrantName I can see the NameResult is set to X number of returns. However within the view the datagrid does not get populated with anything?
Can anyone help me understand what I need to do with the bindings to get the gridview to populate? Other areas of the form which are bound to other collections show data so I know I have the data context of the view set correctly. I've tried both Data context as well as Items Source and no return?
When I set a break on the code the collection is returned as follows so I can see that data is being returned. Any suggestions on what I am missing I would greatly appreciate it.
With regards to the page datacontext I am setting it in the code behind as follows:
var WarrantDetailViewModel = ((ViewModelLocator)App.Current.Resources["Locator"]).WarrantDetailViewModel;
this.DataContext = WarrantDetailViewModel;
this.Resources.Add("vm", WarrantDetailViewModel);
Thanks in advance for any suggestions.
Make ObservableCollection<WarrantNameDataView> NameResult a public property of your ViewModel class. Your view will not be able to bind to something that has a private method scope (or public method scope, or private member scope).
//declaration
public ObservableCollection<WarrantNameDataView> NameResult { get; set }
//in the ViewModel constructor do this
NameResult = new ObservableCollection<WarrantNameDataView>();
//then replace the original line in your method with:
//EDIT: ObservableCollection has no AddRange. Either loop through
//entities and add them to the collection or see OP's answer.
//NameResult.AddRange(entities);
If processWarrantNames gets called more than once, you might need to call NameResult.Clear() before calling AddRange() adding to the collection.
Phil was correct in setting the property to public. One note I'll add is there is no AddRange property in SL or ObservableCollection class that I could find. I was able to assign the entities to the OC using the following code
private ObservableCollection<WarrantNameDataView> warrantNameResult;
public ObservableCollection<WarrantNameDataView> WarrantNameResult
{
get { return warrantNameResult; }
set
{
warrantNameResult = value;
NotifyPropertyChanged(vm => vm.WarrantNameResult);
}
}
and then within the return method
WarrantNameResult = new ObservableCollection<WarrantNameDataView>(entities);
This worked and passed to the UI the collection of data.
I have a grid that is binded to a collection. For some reason that I do not know, now when I do some action in the grid, the grid doesn't update.
Situation : When I click a button in the grid, it increase a value that is in the same line. When I click, I can debug and see the value increment but the value doesn't change in the grid. BUT when I click the button, minimize and restore the windows, the value are updated... what do I have to do to have the value updated like it was before?
UPDATE
This is NOT SOLVED but I accepted the best answer around here.
It's not solved because it works as usuall when the data is from the database but not from the cache. Objects are serialized and threw the process the event are lost. This is why I build them back and it works for what I know because I can interact with them BUT it seem that it doesn't work for the update of the grid for an unkown reason.
In order for the binding to be bidirectional, from control to datasource and from datasource to control the datasource must implement property changing notification events, in one of the 2 possible ways:
Implement the INotifyPropertyChanged interface, and raise the event when the properties change :
public string Name
{
get
{
return this._Name;
}
set
{
if (value != this._Name)
{
this._Name= value;
NotifyPropertyChanged("Name");
}
}
}
Inplement a changed event for every property that must notify the controls when it changes. The event name must be in the form PropertyNameChanged :
public event EventHandler NameChanged;
public string Name
{
get
{
return this._Name;
}
set
{
if (value != this._Name)
{
this._Name= value;
if (NameChanged != null) NameChanged(this, EventArgs.Empty);
}
}
}
*as a note your property values are the correct ones after window maximize, because the control rereads the values from the datasource.
It sounds like you need to call DataBind in your update code.
I am using the BindingSource object between my Collection and my Grid. Usually I do not have to call anything.