Why is Command not invoked when binding value is null? - c#

I have made a grid that I would like to initiate with null or empty values.
But when my textblocks binded value is null the click/tap won't trigger the command.
When I have a value it works fine.
I've also tried to use string.empty and " " which won't work either. Would be happy if somebody could shed some light on this.
XAML
<Grid x:Name="GridPuzzleOuter">
<ItemsControl ItemsSource="{Binding Puzzle.Rows}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Width="270">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Border Grid.Column="0">
<TextBlock Text="{Binding Cells[0].CellValue, Mode=TwoWay}">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Tapped">
<core:InvokeCommandAction
Command="{Binding CellTappedCommand}" CommandParameter="{Binding Cells[0]}"/>
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</TextBlock>
</Border>
<Border Grid.Column="1">
<TextBlock Text="{Binding Cells[1].CellValue, Mode=TwoWay}">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Tapped">
<core:InvokeCommandAction
Command="{Binding CellTappedCommand}" CommandParameter="{Binding Cells[1]}"/>
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</TextBlock>
</Border>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
Rows class
public class Row : ObservableObject
{
public Cell[] Cells { get; set; }
private RelayCommand<Cell> _cellTappedCommand;
public RelayCommand<Cell> CellTappedCommand
{
get
{
return _cellTappedCommand ?? (_cellTappedCommand = new RelayCommand<Cell>((param) => Cellclick(param)));
}
}
private void Cellclick(Cell param)
{
var vm = (new ViewModelLocator()).Main;
vm.SelectedCell = param;
// Do stuff...
}
}
Edit:
Solved by changing XAML into:
<Border Grid.Column="0">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Tapped">
<core:InvokeCommandAction
Command="{Binding CellTappedCommand}" CommandParameter="{Binding Cells[0]}"/>
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
<TextBlock Text="{Binding Cells[0].CellValue, Mode=TwoWay}">
</TextBlock>
</Border>

Just move your interaction code to the surrounding Border and be sure to set the Background property to Transparent. Problem solved.
// Best of luck!

Related

WPF Command binding in button inside a materialDesign dialog

Im new to WPF and material desing. And im trying to make a dialog for a confirmation of a button, but when i bind the command and press the button it never gets to the method.
The dialog is made in a dynamicly populated listBox.
Window
<ListBox Height="585"
Name="NoteList"
ScrollViewer.CanContentScroll="True"
ScrollViewer.VerticalScrollBarVisibility="Visible"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ItemsSource="{Binding dtoRecetas}" Grid.Row="1">
<ListBox.ItemTemplate>
<DataTemplate>
<!--VERSION CARTAS-->
<materialDesign:Card Style="{StaticResource cardsRecetas}" >
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="150"/>
<RowDefinition Height="170"/>
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<!-- DIALOGO DE ELIMINAR RECETA-->
<materialDesign:DialogHost CloseOnClickAway="True" >
<materialDesign:DialogHost.DialogContent>
<StackPanel Margin="16">
<TextBlock>Deseas eliminar esta receta</TextBlock>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<Button Style="{StaticResource MaterialDesignFlatButton}"
IsDefault="True"
Margin="0 8 8 0"
Command="{Binding Path=comandoDeleteReceta}"
CommandParameter="{Binding idReceta}">
Aceptar
</Button>
<Button Style="{StaticResource MaterialDesignFlatButton}"
Margin="0 8 8 0"
Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}">
Cancelar
</Button>
</StackPanel>
</StackPanel>
</materialDesign:DialogHost.DialogContent>
<Button x:Name="btnDeleteReceta" Style="{StaticResource btnDeleteReceta}"
Tag="{Binding idReceta}"
Command="{x:Static materialDesign:DialogHost.OpenDialogCommand}">
<materialDesign:PackIcon Kind="RemoveCircle" />
</Button>
</materialDesign:DialogHost>
</StackPanel>
</Grid>
</materialDesign:Card>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel ScrollViewer.VerticalScrollBarVisibility="Visible" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ></WrapPanel>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
ViewModel
public class VerRecetasViewModel : BaseViewModel
{
public Command comandoDeleteReceta { get; set; }
public ObservableCollection<DTOReceta> dtoRecetas { get; set; }
public VerRecetasViewModel()
{
RecetasController recController = new RecetasController();
dtoRecetas = new ObservableCollection<DTOReceta>(recController.getAllRecetas());
comandoDeleteReceta = new Command();
comandoDeleteReceta.CanExecuteFunc = obj => true;
comandoDeleteReceta.ExecuteFunc = deleteReceta;
}
public void deleteReceta(object parameter)
{
int idReceta = (int)parameter;
RecetasController recController = new RecetasController();
recController.deleteReceta(idReceta);
}
}
The desired behavior is that when they press "Aceptar" it gets to the "deleteReceta" method, but as right now it looks like is not finding the binded command.
The command class is working and i use commands in other windows this way and it works, i think im losing the context from the dialog or creating it wrong. I tried this way and adding relative source to the binding, but i dont know what the ancestortype should be.
Thanks for the help.
I think it's a problem with the DataContext. So I would set the dataContext of the view like this :
d:DataContext="{d:DesignInstance {x:Type vm:VerRecetasViewModel}}"
and in the ListBox, bind the command on the button like this :
Command="{Binding DataContext.comandoDeleteReceta, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}}}"
Hope this helps

Texbox value binding in WPF MVVM

I have sample XAML with TextBox code as:
XAML
<TextBox HorizontalAlignment="Stretch" VerticalAlignment="Center"
Text="{Binding Path=Remarks, UpdateSourceTrigger=PropertyChanged}"
BorderThickness="0.5" Margin="0" Height="50" Background="Transparent" Foreground="White" />
<Button CommandParameter="{Binding ListExecActionId}"
Command="{Binding Source={StaticResource Locator}, Path=TaskPerformanceModel.ActivityAction_comment}"
Content="Save" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="3,0,0,0" Height="Auto" />
View Model:
public string Remarks
{
get { return _remarks; }
set
{
if (!string.Equals(_remarks, value))
{
_remarks = value;
RaisePropertyChanged("Remarks");
}
}
}
ActivityAction_coment as follows
public RelayCommand<object> ActivityAction_comment
{
get
{
if (_ActivityAction_comment == null)
{
_ActivityAction_comment = new RelayCommand<object>((ExecActionId) => ActivityComment(ExecActionId));
}
return _ActivityAction_comment;
}
}
private void ActivityComment(object _id)
{
try
{
using (DataContext objDataContext = new DataContext(DBConnection.ConnectionString))
{
ListExecutionAction tblListExec = objDataContext.ListExecutionActions.Single(p => p.Id == Convert.ToInt32(_id));
**tblListExec.Remarks = Remarks; // Not getting Remarks value from Textbox**
objDataContext.SubmitChanges();
}
}
catch (Exception Ex)
{
MessageBox.Show(Ex.Message, "TaskExecution:ActivityComment");
}
}
I am unable to get textbox (Remarks) value in view model. Always getting "".
can any one help me out please.
For more clarity I am updating view:
<ListView.View>
<GridViewColumn >
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding ActionDescription}" Foreground="White" FontSize="16"></TextBlock>
<ToggleButton Name="button">
<ToggleButton.Template>
<ControlTemplate TargetType="ToggleButton">
<TextBlock>Remarks!!</TextBlock>
</ControlTemplate>
</ToggleButton.Template>
</ToggleButton>
<Popup IsOpen="{Binding IsChecked, ElementName=button}" StaysOpen="False" Width="250" Height="100">
<StackPanel>
<TextBlock Background="LightBlue" Text="{Binding ActionDescription}"></TextBlock>
<TextBlock Grid.Column="0" HorizontalAlignment="Right" VerticalAlignment="Center" Text="Comments:" Foreground="White" Background="Transparent" />
<TextBox HorizontalAlignment="Stretch" VerticalAlignment="Center"
Text="{Binding Path=Remarks, UpdateSourceTrigger=PropertyChanged}"
BorderThickness="0.5" Margin="0" Height="50"/>
<!--Text="{Binding Remarks, Mode=OneWayToSource}" Text="{Binding Path=Remarks, UpdateSourceTrigger=PropertyChanged}" DataContext="{Binding CollectionOfListQueue}" Background="Transparent" Foreground="White"-->
<Button CommandParameter="{Binding ListExecActionId}" Command="{Binding Source={StaticResource Locator}, Path=TaskPerformanceModel.ActivityAction_comment}" Content="Save" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="3,0,0,0" Height="Auto" />
<Button Content="Cancel" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="2,0,0,0" Height="Auto" />
<!--</Grid>-->
</StackPanel>
</Popup>
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
Bind to ActivityAction_comment and Remarks properties of the view model:
<Button CommandParameter="{Binding ListExecActionId}"
Command="{Binding DataContext.ActivityAction_comment, RelativeSource={RelativeSource AncestorType=ListView}}"
Content="Save" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="3,0,0,0" Height="Auto" />
You need to the same for the Remarks binding
<TextBox Text="{Binding DataContext.Remarks, UpdateSourceTrigger=PropertyChanged, RelativeSource={RelativeSource AncestorType=ListView}}" ... />
You should then be able to get the value in the TextBox using the Remarks source property:
private void ActivityComment(object _id)
{
try
{
using (DataContext objDataContext = new DataContext(DBConnection.ConnectionString))
{
ListExecutionAction tblListExec = objDataContext.ListExecutionActions.Single(p => p.Id == Convert.ToInt32(_id));
string remarks = Remarks;
objDataContext.SubmitChanges();
}
}
catch (Exception Ex)
{
MessageBox.Show(Ex.Message, "TaskExecution:ActivityComment");
}
}

Treeview contextmenu not working command in HierarchicalDataTemplate

I have a TreeView, which works fine. Within this I have a context menu, which works fine, except for the command which is the issue at hand.
The main parts, I think are here:
<TreeView x:Name="ScenesTreeView01" Grid.Column="0" Width="Auto" Background="AliceBlue" ItemsSource="{Binding Scenes}" SelectedItemChanged="TreeView_SelectedItemChanged" BorderThickness="0">
<TreeView.DataContext>
<viewModels:ScenesViewModel />
</TreeView.DataContext>
<TreeView.Resources>
<LinearGradientBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" StartPoint="0,0" EndPoint="0,1">
<LinearGradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#F7D073" Offset="0"/>
<GradientStop Color="#F1A62F" Offset="1"/>
</GradientStopCollection>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
<ContextMenu x:Key="SceneLevel">
<MenuItem Header="Add selected character" Command="{Binding AddSelectedCharacter}"/>
</ContextMenu>
<ContextMenu x:Key="CharacterLevel">
<MenuItem Header="Character Level"/>
</ContextMenu>
</TreeView.Resources>
And here:
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Characters}">
<StackPanel Orientation="Horizontal" ContextMenu="{StaticResource SceneLevel}">
<TextBlock Text="{Binding SceneName}"></TextBlock>
<Image Source="{StaticResource ImgBook1}" Margin="0,0,5,0" Width="32" Height="32"/>
</StackPanel>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<Border BorderThickness="2" BorderBrush="LightBlue" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="2" CornerRadius="5,5,5,5">
<StackPanel Orientation="Horizontal" Margin="3" ContextMenu="{StaticResource CharacterLevel}">
<TextBlock FontFamily="Levenim MT" FontSize="16" VerticalAlignment="Center" MinWidth="50" Text="{Binding FirstName}"></TextBlock>
<Image Source="{Binding ImgIcon}" Margin="2" Width="32" Height="32"/>
</StackPanel>
</Border>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
The second one's not really important, but it shows where the two context menus are used. As I said, they appear as expected.
As for my command... I have used commands before in this project and use them at other points in this project. I use MicroMVVM's 'RelayCommand' to create them and they have worked fine so far.
The command in question sits inside the 'ScenesViewModel' class which is, as you might imagine, a view model.
The code for the command is here:
void AddSelectedCharacterExecute()
{
MessageBox.Show("Adding character");
return;
}
bool CanAddSelectedCharacterExecute()
{
return true;
}
public ICommand AddSelectedCharacter { get { return new RelayCommand(AddSelectedCharacterExecute, CanAddSelectedCharacterExecute); } }
It doesn't really do much at the moment, but I can't even get the MessageBox to appear. Break points show that code is not reached when I click on the context menu item.
Have I missed something obvious here? Or is it something in the process? A vague thought is that, because the ViewModel is attached to the TreeView, the TreeView.Resources might not know about it yet? I'm clutching at straws I guess.
Any help would be appreciated.
You should be able to bind to a property of the ScenesViewModel using an {x:Reference}:
<TreeView.Resources>
...
<ContextMenu x:Key="SceneLevel">
<MenuItem Header="Add selected character" Command="{Binding Path=DataContext.AddSelectedCharacter, Source={x:Reference ScenesTreeView01}}"/>
</ContextMenu>
...
</TreeView.Resources>
Forget my comment. My brain was somewhere else.
To do what you want, you have to do some hackish stuff, since a ContextMenu is not part of the VisualTree.
Following you find a small example:
Xaml
<Grid>
<TreeView ItemsSource="{Binding Scenes}" x:Name="rootView">
<TreeView.Resources>
<ContextMenu x:Key="SceneLevel" DataContext="{Binding RelativeSource={RelativeSource Self}, Path=PlacementTarget.Tag}">
<MenuItem Header="Add selected character" Command="{Binding AddSelectedCharacter}"/>
</ContextMenu>
<ContextMenu x:Key="CharacterLevel">
<MenuItem Header="Character Level"/>
</ContextMenu>
</TreeView.Resources>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Characters}">
<StackPanel Orientation="Horizontal" ContextMenu="{StaticResource SceneLevel}" Tag="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type TreeView}}, Path=DataContext}">
<TextBlock Text="{Binding Name}"></TextBlock>
</StackPanel>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<Border BorderThickness="2" BorderBrush="LightBlue" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="2" CornerRadius="5,5,5,5">
<StackPanel Orientation="Horizontal" Margin="3" ContextMenu="{StaticResource CharacterLevel}">
<TextBlock FontFamily="Levenim MT" FontSize="16" VerticalAlignment="Center" MinWidth="50" Text="{Binding Name}"></TextBlock>
</StackPanel>
</Border>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Grid>
Code-Behind
public partial class Window2 {
public Window2() {
InitializeComponent();
for (int i = 0; i < 10; i++) {
var s = new Scene() { Name = "Scene " + i };
for (int j = 0; j < 2; j++) {
var c = new Character() { Name = "Character " + j };
s.Characters.Add(c);
}
this.Scenes.Add(s);
}
this.DataContext = this;
}
void AddSelectedCharacterExecute(object param) {
MessageBox.Show("Adding character");
return;
}
public ICommand AddSelectedCharacter {
get {
return new RelayCommand(AddSelectedCharacterExecute, o => true);
}
}
private List<Scene> _scenes = new List<Scene>();
public List<Scene> Scenes => this._scenes;
}
public class Scene {
public Scene() {
this.Characters = new List<Character>();
}
public string Name {
get; set;
}
public List<Character> Characters {
get;
}
}
public class Character {
public string Name {
get; set;
}
}
As you can see, i abuse the Stackpanels Tag as Placeholder for the DataContext. Now i have access in the ContextMenu to the PlacementTarget (which is the StackPanel). After this workaround, the rest should be simple.
Hope this helps! Cheers

How to attach trigger on ControlTemplate items?

We are trying to bind Trigger on Stackpanel loaded while using RadMessageBox control. Example :
<!-- CustomMessageBox Template -->
<ControlTemplate x:Key="MessageBoxTemplate" TargetType="messageBox:RadMessageBoxControl">
<Border Padding="12" Background="{StaticResource PhoneChromeBrush}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ContentControl x:Name="PART_Title" Grid.Row="0"
HorizontalContentAlignment="Left"
FontSize="{StaticResource PhoneFontSizeLarge}"
FontFamily="{StaticResource PhoneFontFamilySemiBold}"
Margin="{StaticResource PhoneMargin}"/>
<ContentControl HorizontalContentAlignment="Left" Grid.Row="1"
VerticalContentAlignment="Top" Margin="{StaticResource PhoneMargin}"
x:Name="PART_Message"/>
<CheckBox x:Name="PART_CheckBox" Grid.Row="2"
HorizontalAlignment="Left"
VerticalAlignment="Bottom"/>
<ContentControl x:Name="PART_ButtonsContainer" Grid.Row="3"
HorizontalContentAlignment="Stretch" Margin="12,0" Width="440">
<ContentControl.ContentTemplate>
<DataTemplate>
<StackPanel x:Name="PART_ButtonsPanel"
Orientation="Vertical" HorizontalAlignment="Stretch">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding DataContext.CustomMessageBoxStackPanelLoadedCommand}" CommandParameter="{Binding PART_ButtonsPanel}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</StackPanel>
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
</Grid>
</Border>
</ControlTemplate>
The ControlTemplate is inside Page.Resources.
We are unable to Trigger stackpanel loaded event. Code behind file :
private bool _CustomMessageBoxStackPanelLoadedCommandCanExecute = true;
bool CustomMessageBoxStackPanelLoadedCommandCanExecute
{
get
{
return _CustomMessageBoxStackPanelLoadedCommandCanExecute;
}
set
{
if (_CustomMessageBoxStackPanelLoadedCommandCanExecute == value)
{
return;
}
_CustomMessageBoxStackPanelLoadedCommandCanExecute = value;
RaisePropertyChanged("CustomMessageBoxStackPanelLoadedCommandCanExecute");
if (_CustomMessageBoxStackPanelLoadedCommand != null)
_CustomMessageBoxStackPanelLoadedCommand.RaiseCanExecuteChanged();
}
}
private RelayCommand<string> _CustomMessageBoxStackPanelLoadedCommand;
public ICommand CustomMessageBoxStackPanelLoadedCommand
{
get
{
if (_CustomMessageBoxStackPanelLoadedCommand == null)
{
_CustomMessageBoxStackPanelLoadedCommand = new RelayCommand<string>(CustomMessageBoxStackPanelLoaded, (data) => CustomMessageBoxStackPanelLoadedCommandCanExecute);
}
return _CustomMessageBoxStackPanelLoadedCommand;
}
}
private void CustomMessageBoxStackPanelLoaded(object obj)
{
System.Diagnostics.Debug.WriteLine("Hello");
}
The problem is with your CommandBinding. your binding is:
Command="{Binding DataContext.CustomMessageBoxStackPanelLoadedCommand}"
that means that in the DataContext class of your control there is any object name DataContext which has CustomMessageBoxStackPanelLoadedCommand as a property in it. But that is not the case. Such type of syntax is used in RelativeSource Binding.
But here change your binding as:
Command="{Binding CustomMessageBoxStackPanelLoadedCommand}"
So binding engine will now find this command directly inside your DataContext. (I hope there is not any problem with giving datacontext to your views.)
also not sure why you have written
CommandParameter="{Binding PART_ButtonsPanel}"
like this. what are you trying to do there?

BindingExpression Path Error - How to structure code in MVVM model?

I have a Button that when clicked should adjust the Widths of two Grids.
<DataTemplate x:Key="ItemTemplate">
<DockPanel Width="Auto">
<Button Click="SelectMovie_Click" DockPanel.Dock="Top">
<Button.Template>
<ControlTemplate >
<Image Source="{Binding image}"/>
</ControlTemplate>
</Button.Template>
<i:Interaction.Behaviors>
<local:UniqueNameBehavior ID="{Binding id}"/>
</i:Interaction.Behaviors>
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonDown">
<i:InvokeCommandAction Command="{Binding ShowRightGridCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
<TextBlock Text="{Binding title}" HorizontalAlignment="Center" DockPanel.Dock="Bottom"/>
</DockPanel>
</DataTemplate>
This Button is displayed within a Grid as such:
<Grid Grid.Row="2" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding LeftGridWidth}" />
<ColumnDefinition Width="{Binding RightGridWidth}" />
</Grid.ColumnDefinitions>
// BUTTONS DISPLAYED HERE
<Grid x:Name="LeftGrid" Grid.Row="2" Grid.Column="0" >
<Border BorderThickness="1" BorderBrush="Red">
<ItemsControl ItemTemplate="{StaticResource ItemTemplate}" ItemsSource="{Binding _movies}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="5" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Border>
</Grid>
<Grid x:Name="RightGrid" HorizontalAlignment="Left" Grid.Row="2" Grid.Column="1" >
<DockPanel>
<StackPanel VerticalAlignment="Top" Height="200">
<TextBlock Width="200" Height="50" DockPanel.Dock="Top" HorizontalAlignment="Left">
<TextBlock.Text>
<MultiBinding StringFormat="{}{0} ({1})">
<Binding Path = "SelectedMovie.title"/>
<Binding Path = "SelectedMovie.year"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</StackPanel>
<StackPanel VerticalAlignment="Top" Height="200">
<Grid HorizontalAlignment="Center">
<Image Source="star_icon.png" Width="100" Height="100" VerticalAlignment="Top"/>
<TextBlock Text="{Binding SelectedMovie.rating}" Style="{StaticResource AnnotationStyle}" Width="150"/>
</Grid>
</StackPanel>
</DockPanel>
</Grid>
</Grid>
ViewModel
public List<MediaDetail> _movies { get; set; }
public string selectedMovieID { get; set; }
private GridLength _leftGridWidth;
private GridLength _rightGridWidth;
private readonly GridLength _defaultRightGridWidth = new GridLength(0, GridUnitType.Pixel);
public MoviePanelViewModel()
{
ShowRightGridCommand = new DelegateCommand(SwitchRightGridWidth);
LeftGridWidth = new GridLength(7, GridUnitType.Star);
RightGridWidth = _defaultRightGridWidth;
}
private void SwitchRightGridWidth()
{
RightGridWidth = RightGridWidth == _defaultRightGridWidth ? new GridLength(3, GridUnitType.Star) : _defaultRightGridWidth;
}
public GridLength LeftGridWidth
{
get { return _leftGridWidth; }
set { _leftGridWidth = value; OnPropertyChanged("LeftGridWidth"); }
}
public GridLength RightGridWidth
{
get { return _rightGridWidth; }
set { _rightGridWidth = value; OnPropertyChanged("RightGridWidth"); }
}
public ICommand ShowRightGridCommand { get; set; }
The problem is that when I run my code, I get the error:
BindingExpression path error: 'ShowRightGridCommand' property not found on 'object' ''MediaDetail'
I am not sure how to structure my code such that the Width properties for the Grids can remain in the ViewModel but the Trigger for my Button also works. The DataContext for my View is the ViewModel.
Is there a good way to achieve this?
Thank you for your help.
The problem is your binding inside a DataTemplate: WPF tries to find the property path ShowRightGridCommand on your MediaDetail class, because the ItemsControl sets the data context for each item container it generates to the corresponding item.
What you actually want to do is binding to the view model. You can do this like so:
{Binding ElementName=LeftGrid, Path=DataContext.ShowRightGridCommand}
LeftGrid is an element outside of the ItemsControl. Its DataContext is your MoviePanelViewModel instance, and there is your ShowRightGridCommand property defined.
A little cleaner would be to put the name root on your view's root control (usually a UserControl or Window), and then use ElementName=root,Path=DataContext.... as your binding - at least that's what I usually do.
An alternative would be to use RelativeSource={RelativeSource UserControl}, but I find ElementName=root to be simpler.

Categories