Deleting row in listview using Command + CommandParameter in WPF + MVVM - wpf

I want to delete a row in ListView Control using Command and CommandParameter like below.
<GridViewColumn Header="X">
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding CriteriaId}"/>
<Button Name="btnDeleteCriterion" Tag="{Binding CriteriaId}" Content="{Binding CriteriaId}" Foreground="Red" FontWeight="Bold"
Command="{Binding DeleteCriterionCommand}"
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType=ListView}}"
CommandParameter="{Binding RelativeSource={RelativeSource self}, Path=Tag}"
/>
<StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
I am trying to grab the Tag property of the Button and pass it to Command like above and than remove it from list like so.
Edited, above XAML and added a TextBlock which uses the same binding as the Button's Tag and Content, but somehow Button doesn't get the value but TextBlock does!?
public void DeleteCriterion(object criterionId)
{
int crtId = (int)criterionId;
Criterion crt = _criteria.FirstOrDefault((c) => c.CriterionId == crtId);
if (crt != null)
_criteria.Remove(crt);
}
but I always get criterionId parameter as null.
What am I doing wrong?

Since you're explicitly setting the DataContext within the button, when you're doing a binding like {Binding SomeProperty} it will assume that SomeProperty is in the DataContext that you just set. Try using a more explicit binding like:
"{Binding RelativeSource={RelativeSource AncestorType=StackPanel}, Path=DataContext.CriteriaId}"
which will give you the correct DataContext's CriteriaID like it is for the TextBlock.

As long as your button's Tag isn't null (if it is null, you have a different problem), I don't see a reason that you can't bind directly to CriterionId like you do with Tag.

Related

Binding only works using ElementName, why?

Here is some code snippets of my scenario. On the .xaml side I have an ItemsControl:
<ItemsControl ItemsSource="{Binding StatList}" ItemTemplate="{StaticResource statBox}"></ItemsControl>
The StatList is simply a List of objects.
The DataTemplate contains this TextBlock
<TextBlock x:Name="DataTextBlock" Background="Transparent" DockPanel.Dock="Top" HorizontalAlignment="Center"
Foreground="White" FontSize="11">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<iBehaviors:InvokeMethodTrigger Method="UpdateBinding"
Target="{Binding DataContext, RelativeSource={RelativeSource TemplatedParent}}"
Parameter="{Binding ., RelativeSource={RelativeSource AncestorType=TextBlock}}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBlock>
This works the first time I set my StatList on my ViewModel, the TextBlock itself is passed as the Parameter to the InvokeMethodTrigger. However, if I change the StatList on my viewmodel, the next time the Loaded event occurs, the Parameter is null when the InvokeMethodTrigger is triggered.
Oddly enough, when I change the Parameter binding to:
Parameter="{Binding ElementName=DataTextBlock}"
it works the second time when I change my StatList, with the TextBlock being passed as the parameter. I for my life can't figure out why! Any explanations?
You can do it like this in code behind:
SlatList slat = new SlatList();
ItemControl.ItemSource = slat;
ItemControl.DataContext = slat;
make sure to specify a name for the itemcontrol and replace "ItemControl" with the name.
In XAML:
<ItemsControl ItemsSource="{Binding StatList}" DataContext="{Binding SlatList} ItemTemplate="{StaticResource statBox}"> </ItemsControl>

Bind to a Property in the Data Context from a ListviewItem

I have a data context in the form of MyViewModel.
MyViewModel has a property: public int MyWidth.
For each item in my listview, that is ListViewItem, I need to display a canvas with a width equal to MyWidth.
My listView has it's ItemSource bound to a property called MyCollectionOfInts.
Which, as you may have guessed, is of the following definition: ObservableCollection<int>.
The astute reader has likely realized, the data context of myListView is int and thus fails when trying to bind the non-existant MyWidth property from the int type data-context.
What kind of theoretical crazy binding is necessary to get this kind of nutty thing to work?
My most recent attempt was to bind using RelativeSource but couldn't exactly figure it out...
My list View:
<ListView Name="MyListView" ItemsSource="{Binding MyCollectionOfInts}"
My Items within the List view.
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumns Header=MyInts">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Label Name="m_TestLabel" Content="ASDF" />
<TextBox Text="{Binding Path=MyWidth, RelativeSource=????{RelativeSource AncestorType={x:Type MyViewModel}}}"/>
</DataTemplate>
</...a bunch of close brackets>
My list view Item is an int, but I want to get the Control's original data-context, ie. myViewModel, and bind the canvas width of my ListViewItem to the MyWidth property of myViewModel. How can I get the ListViewItem to recognize the control's data-context?
Note: I don't really want to make a container for the ListView and store a static MyWidth variable in it, but if that is the only way, then let me know. I'm hoping it's not.
Try this:
<TextBox Text="{Binding Path=DataContext.MyWidth, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"/>
If your View is usercontrol else use Window in type
<TextBox Text="{Binding Path=DataContext.MyWidth,
RelativeSource={RelativeSource AncestorType={x:Type ListView }}}"/>
Or
<TextBox Text="{Binding Path=DataContext.MyWidth,
Source={x:Reference MyListView}"/>

Binding ListBox Items using Templates

I am going crazy trying to figure this out without success.
I have a DependencyObject, ("UserObject"). It has a "DataItems" DependecyProperty that is an ObservableCollection. "UserDefiniton" is a DependencyObject with a DependencyProperty of "Data". Data has two properties: DataType (an enumeration) and Value (a string).
I am trying to define a ListBox in XAML that uses the "DataItems" property as its ItemsSource. In the ItemTemplate, I have several different controls. For simplicity of this issue, I am using a CheckBox and a TextBox. I want CheckBox to be available and visible when DataType is 0, while I want the TextBox to be available and visible when the DataType is 1. Only one control can be available and visible at a time.
This works:
<ListBox
ItemsSource={Binding DataItems, Mode=OneWay}>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<CheckBox
Visibility="{Binding Path=Data.DataType, Mode=OneWay, Converter={StaticResource VisibilityConverter}, ConverterParameter=0}"
IsChecked="{Binding Path=Data.Value, Mode=TwoWay, Converter={StaticResource StringToBoolean}}" />
<TextBox
Visibility="{Binding Path=Data.DataType, Mode=OneWay, Converter={StaticResource VisibilityConverter}, ConverterParameter=1}"
Text="{Binding Path=Data.Value, Mode=TwoWay}" />
</StackPanel>
</DataTemplate>
<Listbox.ItemTemplate>
</ListBox>
The problem is that even though only one is visible, both are fighting over the Data.Value property (the boolean of the checkbox will show in the textbox, even though the checkbox is hidden).
Basically, though, the binding in this case is working--but the implementation is incorrect.
So, I switched to using Templates. The problem I am having is that I can't get the binding to work.
This is the code that I have for the Template. The Template selector is working correctly, but the Text property of the TextBox and the IsChecked property of the checkbox are not binding to Data.Value:
<DataTemplate x:Key="TextBoxItem">
<TextBox
Text="{Binding Path=Data.Value, Mode=TwoWay}" />
</DataTemplate>
<DataTemplate x:Key="CheckBoxItem">
<CheckBox
IsChecked="{Binding Path=Data.Value, Mode=TwoWay, Converter={StaticResource StringToBoolean}}" />
</DataTemplate>
...
<ListBox
ItemsSource={Binding DataItems, Mode=OneWay}>
<ListBox.ItemTemplate>
<DataTemplate>
<ContentControl
Content="{Binding Path=Data.DataType, Mode=OneWay}"
ContentTemplateSelector="{DynamicResource UserDefinitionTemplateSelector}"/>
</DataTemplate>
<ListBox.ItemTemplate>
</ListBox>
So how do I fix the binding?
Content should be set to {Binding}, since the Content will be the DataContext of the data-templates, hence you should just pass on the current DataContext. If you need to pass specific data to the template selector you can just drill down in the whole object.
There also is a template selector on the level of the ListBox, so you do not really need the internal ContentControl.
(You might also be interested in generic methods of debugging data bindings.)

Lookup-id-control in WPF DataGrid

I am working with DataGrid. One column displays text, but the data behind this contains only an id. This id must somehow be converted to a string.
I need something like a combobox with the properties ItemsSource, DisplayMemberPath, SelectedValue and SelectedValuePath. But instead of a button displayed, there must be only a text. Is there some control for that?
That works (would like to exchange combobox with something that looks like textbox):
<DataGridTemplateColumn Header="Leistungsart" MinWidth="100">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Source={StaticResource ResourceKey=viewModel}, Path=Leistungsarten}"
DisplayMemberPath="Bezeichnung"
SelectedValue="{Binding Path=BDELeistungsartID, Mode=OneWay, Converter={StaticResource ResourceKey=NullableInt2IntConverter}}"
SelectedValuePath="BDELeistungsartID"
IsEnabled="false"
IsEditable="False"
Height="35">
</ComboBox>
</DataTemplate>
Thanks a lot for your aswer. Yes, that with Template property worked for me:
<ComboBox ItemsSource="{Binding Source={StaticResource ResourceKey=viewModel}, Path=Leistungsarten}"
DisplayMemberPath="Bezeichnung"
SelectedValue="{Binding Path=BDELeistungsartID, Mode=OneWay, Converter={StaticResource ResourceKey=NullableInt2IntConverter}}"
SelectedValuePath="BDELeistungsartID">
<ComboBox.Template>
<ControlTemplate>
<Label Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=SelectedItem.Bezeichnung}"
Margin="0,0,0,0" Padding="0,0,0,0"/>
</ControlTemplate>
</ComboBox.Template>
</ComboBox>
You could use a ComboBox, but overwrite the Template property to show just a Label. You'll have to recreate the Click events too.
Easiest way would be to use a tool like Snoop or Blend and see what the default ComboBox template looks like, and modify that to what you want.

Bind to ItemsControl's DataContext from inside an ItemTemplate

I have an ItemsControl whose for the ItemTemplate DataTemplate contains a Button. I want the Command on the button to bind to a Command on the DataContext of the ItemsControl, not the ItemTemplate. I think the solution has to do with using RelativeSource, but my attempts so far have failed:
<ItemsControl ItemsSource="{Binding Games}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Command="{Binding Path=GameSelectedCommand, Source={RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}}}"
CommandParameter="{Binding}"
Style="{StaticResource MenuButtonStyle}"
Content="{Binding Name}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
How can I get the Button to bind to the GameSelectedCommand of the ItemsControl's DataContext object?
You're setting the source of the binding to the ItemsControl itself. Therefore, you'll need to dereference the DataContext of the ItemsControl:
Command="{Binding DataContext.GameSelectedCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}}}"
How would you have known this? Take a look at your debug output window when running the app. You'll see a message along the lines of "Cannot resolve property 'GameSelectedCommand' on type 'ItemsControl'".

Resources