I have a silverlight application. One of StackPanel will display the table. The first column is a check box.
<telerik:RadGridView.Columns>
<telerik:GridViewColumn Width="80" Header="Complete" HeaderTextAlignment="Center" TextAlignment="Center">
<telerik:GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox HorizontalAlignment="Center" IsChecked="{Binding Something, Converter={StaticResource ShortToBooleanConverter}}" Checked="Complete_Checked"></CheckBox>
</DataTemplate>
</telerik:GridViewColumn.CellTemplate>
</telerik:GridViewColumn>
What I want is that once I click the box, a message box with Y/N pops up. I do have a Complete_Checked method in MVVM. But I get the error
Event handler 'Complete_Checked` not found on class.....
You can't use click event handlers with MVVM you need to use CommandBinding or DataBinding depending on what you're doing.
With your example you'll use data binding. You want to bind to the checkbox dependency property called IsChecked. You'll also want to use the Mode of TwoWay. This will allow the UI to update the bound property when it changes.
<CheckBox IsChecked="{Binding CheckBoxIsChecked, Mode=TwoWay}">
Then in your object model not viewmodel
private bool _checkBoxIsChecked;
public bool CheckBoxIsChecked
{
get{ return _checkBoxIsChecked;}
set{_checkBoxIsChecked = value; OnPropertyChanged("CheckBoxIsChecked"); }
}
Related
What is the proper way (MVVM) to handle following situation? We have an window/user control which hosts few user controls and grid. When we select grid item, SelectedItem="{Binding SelectedAccount, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" is updating SelectedAccount property on user controls
<TabItem Header="{x:Static p:Resources.Basic}">
<DockPanel>
<accounts:UCBasic x:Name="UCBasic" SelectedAccount="{Binding SelectedItem, ElementName=gridMain}"></accounts:UCBasic>
</DockPanel>
</TabItem>
<TabItem Header="{x:Static p:Resources.AdditionalData}">
<DockPanel>
<accounts:UCAdditionalData x:Name="UCAdditionalData" SelectedAccount="{Binding SelectedItem, ElementName=gridMain}"></accounts:UCAdditionalData >
</DockPanel>
... more user controls ...
</TabItem>
using their DependencyProperty. Now, how would I write PageModel for above user controls (UCBasic, UCAdditionalData) so they can load/show more data depending on SelectedAccount from grid. There is dirty way of using property changed event but I don't think it should be done that way. Each user control has this:
public Account SelectedAccount
{
get { return (Account)GetValue(SelectedAccountProp); }
set
{
SetValue(SelectedAccountProp, value);
}
}
public static readonly DependencyProperty SelectedAccountProp = DependencyProperty.Register("SelectedAccount", typeof(Account), typeof(UCBasic));
Essentialy, how I would notify this user control that SelectedAccount value is changed and it should update itself (its own textboxes, grids and so on)?
if each user control has Account property, it can do bindings in its own textboxes, grids and so on, e.g.
<TextBox Text="{Binding Account.Name, RelativeSource={RelativeSource AncestorType=UserControl}}"/>
1) you can use INotifyPropertyChanged implementation in your ViewModel (if will send notification to update changed ViewModel property on View)
2) If you use one ViewModel for both user controls 1 option should help you immediately.
If you use different ViewModels you should update the secornd user control view model in code when the first user control is updated.
I have a Grid on a wpf window which I want to add the capability that user can delete some of the items by clicking on a delete button. The application uses Calibrun Micro to bind view to ViewModel.
My question?
1- Is it a good idea to use a button to delete an item from a grid in WPF?
2- How can I bind a button to a method on VM and in the methd get a pointer to the item that should be deleted?
Edit1
I added the buttons in this way to datagrid:
<DataGridTemplateColumn Width="100">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Content="Delete" cal:Message.Attach="DeleteFromList($dataContext)" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
and c# code follow:
public void DeleteFromList(object tmp)
{
}
But the buttons on datagrid are disabled and clicking on them doesn't fire DeleteFromList method (I checked using debugger).
Why they are disabled? How can I make them enabled?
This depends on how your button is placed - is there a single 'delete' button or have you added a button per row in the grid (are we talking DataGrid or just Grid?)
Assuming you are talking about DataGrid, you can easily just add an action message command to the button and pass through the item which is being deleted to the message handler on the VM
e.g. in the VM
public class MyViewModel
{
public DataItemCollectionTypeName ItemCollection { get; set; }
public void DeleteItem(DataItemTypeName item)
{
ItemCollection.Remove(item);
}
}
Assuming ItemCollection is bound to the grid, the button XAML may look like this:
<Button cal:Message.Attach="[Click] = [DeleteItem($datacontext)]" />
You may also need to set Action.TargetWithoutContext (it should be bound to the VM) if this is a templated row, as otherwise CM will not be able to locate the VM to invoke the action message on
If you have a single button that isn't contained within the grid you can always target the grids SelectedItem in the action message
<DataGrid x:Name="SomeDataGrid"></DataGrid>
<Button cal:Message.Attach="[Click] = [DeleteItem(SomeDataGrid.SelectedItem)]" />
It may be (and probably is) the default property that CM will look at so you may not need to specify the property name unless you have modified default conventions
<DataGrid x:Name="SomeDataGrid"></DataGrid>
<Button cal:Message.Attach="[Click] = [DeleteItem(SomeDataGrid)]" />
Edit
To clarify: In order for CM to find a VM to call the DeleteItem method it uses the DataContext of the current item. In the case of an ItemsControl derived control, the datacontext for each item points to the item being bound, not the ViewModel.
In order to give CM a hint as to which object it should try to resolve the DeleteItem method on, you can use the Action.TargetWithoutContext attached property, which applies a target object for action messages without changing the DataContext of the bound row/item
You can use element name syntax to point to the correct place:
In this example I've used a grid as the root element and named it LayoutRoot, then I've pointed the action message target to LayoutRoot.DataContext (which will be the ViewModel) using ElementName syntax. You can use any method (AncestorType or whatever)
<Grid x:Name="LayoutRoot">
<DataGridTemplateColumn Width="100">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Content="Delete" cal:Message.Attach="DeleteFromList($dataContext)" cal:Action.TargetWithoutContext="{Binding DataContext, ElementName=LayoutRoot}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</Grid>
That should then work!
You could do something like this...
<Button cal:Message.Attach="[Event MouseEnter] = [Action Save($this)]">
Check the docs as they will explain what you need to do and should answer your question: link
I have a ListView control that contains one column with checkboxes only. Is it possible to give those checkboxes some names (indexes - like 1, 2, 3...)?
I need it because I want to identify a concrete checkbox in ToggleButton_OnCheckedUnchecked event in some way.
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Value, Mode=OneWay}" IsThreeState="False"
Checked="ToggleButton_OnCheckedUnchecked"
Unchecked="ToggleButton_OnCheckedUnchecked"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
Don't do it that way. Instead, have each line in your ListView have an IsChecked property, and change the IsChecked Binding to TwoWay. That way, you don't need to use the Checked and UnChecked events at all.
public class LineViewModel
{
public bool IsChecked
{
get { return _isChecked;
}
set
{
// do something here
}
}
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsChecked, Mode=TwoWay}" IsThreeState="False"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
For best results, implement INotifyPropertyChanged too.
Probably dynamic binding some names can be done through the Binding, like that:
<CheckBox Name="{Binding IdCheckBox}" ... />
Quote from the MSDN:
Data binding a Name is technically possible, but is an extremely uncommon scenario because a data-bound Name cannot serve the main intended purpose of the property: to provide an identifier connection point for code-behind.
In short, the Binding Name property for control is impossible and undesirable. But you can use the attached dependency property, like this:
<CheckBox local:GiveName.Name="{Binding Template_Name1}" ... />
In any case, this is not the solution to your problems.
I have a grid of CheckBoxes in a WPF C# project. Each CheckBox's Command property is bound to a CheckBoxChangedCommand in my WView.xaml file, like so:
<CheckBox Grid.Row="0" IsChecked="true" x:Name ="CheckBox0"
Command="{Binding CheckBoxChangedCommand}" />
<CheckBox Grid.Row="1" IsChecked="true" x:Name="CheckBox1"
Command="{Binding CheckBoxChangedCommand}" />
Each time one of the CheckBoxes is either checked or unchecked, I call CheckBoxChanged. How would I go about displaying a pop-up window showing either 1. the row number in the grid of the CheckBox and the name of the CheckBox ("CheckBox0", for example) and 2. The Checked value (true/false) for the checkbox?
My CheckBoxChangedCommand, in WViewModel.cs file, looks like this:
public ICommand CheckBoxChangedCommand
{
get
{
return new RelayCommand(param =>
{
MessageBox.Show("CheckBoxChanged!");
});
}
}
How can I access the IsChecked property and the row number of the CheckBox that triggered CheckBoxChanged from withinCheckBoxChanged? How can I pass the data from my View to my ViewModel?
You definitely need to do more with binding here.
First of all, you should probably be binding the IsChecked property of your Checkboxes to a property on your viewmodel.
Second, based on your comment about needing to know the row number of the checkbox that was checked, I'd say you probably need to be generating the "row" including the CheckBox via databinding, so that you can then pass the object that represents the row as the CommandParameter to your CheckBoxChangedCommand.
So something like:
<ListBox ItemsSource="{Binding MyItems}" />
and then in your resources:
<DataTemplate DataType="{x:Type local:MyItemType}">
<CheckBox IsChecked="{Binding IsChecked}"
Command="{Binding CheckChangedCommand}"
CommandParameter="{Binding}" />
</DataTemplate>
Note: Your CheckChangedCommand is probably on the main ViewModel, not the item-level ViewModel, so you probably need to do some other type of lookup to make it find it - this example is just for simplicity
Let's say I currently have an ItemsControl whose DataTemplate is a bunch of buttons. I'm wiring up these buttons' click events, but how am I to know which button was clicked? Should I not use a ItemsControl?
I'm trying to have no code-behind, but being pragmatic may be necessary.
<ItemsControl>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Margin="10">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<GalaSoft_MvvmLight_Command:EventToCommand Command="{Binding ItemsControlButtonClicked, Mode=OneWay}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
If you want to know what Item was clicked, then pass {Binding } as the CommandParameter and it will pass the selected object to your Command
If you want to know what Button was clicked, I would do that in the code-behind since ViewModels do not need to know anything about the UI, and that includes buttons.
Also since your control is a Button, you should use the Command property instead of a Click trigger.
<Button Command="{Binding ItemsControlButtonClicked}" />
You can send parameters along with the command and based on these parameters you can find out which button was clicked
In my project I also use the MVVM Light I has an dropdown with collection of items, and a button which user press and action depend on selected item from drop down
you should create a Relay command with parameter look at the example from my code
public RelayCommand<Project> StartTimer { get; private set; }//declare command
StartTimer = new RelayCommand<Project>(OnStartTimer);
private void OnStartTimer(Project project)
{
if (project != null)
{
currentProject = project;
if (!timer.IsTimerStopped)
{
timer.StopTimer();
}
else
{
Caption = "Stop";
timer.StartTimer();
}
}
on the view I bind the drop down with collection of class Project
and for button command parameter I bind the selected item form drop down
look at the code
<ComboBox Name="projectcomboBox" ItemsSource="{Binding Path=Projects}" IsSynchronizedWithCurrentItem="True" DisplayMemberPath="FullName"
SelectedValuePath="Name" SelectedIndex="0" >
</ComboBox>
<Button Name="timerButton" Content="{Binding Path=Caption}" Command="{Binding Path=StartTimer}"
CommandParameter="{Binding ElementName=projectcomboBox, Path=SelectedItem}" ></Button>
pay attention to Command and CommandParameter binding
also you can use this approache not only for drop down
Well, you can use the Sender.DataContext which is the actual data.
Create command properties in your view model class (using Josh Smith's RelayCommand pattern is the simplest way to do this) and bind each button's Command to the appropriate one. Not only is this straightforward to do and simple to maintain, it also gives you an easy way of implementing the enable/disable behavior when you need to.