WPF Command binding in button inside a materialDesign dialog - c#

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

Related

Binding parameter to a nested ListView using SelectedItem

I'm pretty new to coding in c# and I'm trying on a wpf app to transfer data between different folders.
To visualize the folders and subfolders I've got a tabcontrol with different mainfolders, and under each tab a ListView with information on the subfolders.
All data is gathered in a BindingList 'Klinieken', which is filled with objects 'Kliniek' (mainfolders) which contains objects 'Patient' (subfolders) which is filled with information about said folder. Here is the .xaml file for the mainwindow:
<Grid x:Name="LayoutRoot" Background="#555555">
<Grid.RowDefinitions>
<RowDefinition Height="420" />
<RowDefinition Height="116" />
</Grid.RowDefinitions>
<Grid x:Name="KliniekTabs" Background="#555555" Grid.Row="0" Margin="10 10 10 10">
<TabControl ItemsSource="{Binding Klinieken}"
SelectedItem="{Binding BronKliniek}"
TabStripPlacement="Top">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding KliniekNaam}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<ListView ItemsSource="{Binding Path=Patienten}"
SelectedItem="{Binding Path=SelectedPatient}">
<ListView.Resources>
<Style TargetType="{x:Type GridViewColumnHeader}">
<Setter Property="HorizontalContentAlignment" Value="Left"/>
</Style>
</ListView.Resources>
<ListView.View>
<GridView>
<GridViewColumn Header="Patientnaam"
Width="250"
DisplayMemberBinding="{Binding PatientNaam}"/>
<GridViewColumn Header="Zisnr"
Width="250"
DisplayMemberBinding="{Binding PatientZis}"/>
<GridViewColumn Header="Aanmaakdatum"
Width="250"
DisplayMemberBinding="{Binding AanmaakDatum}"/>
</GridView>
</ListView.View>
</ListView>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
<Grid x:Name="WindowControls" Background="#777777" Grid.Row="1" Margin="100 5 100 10">
<StackPanel Orientation="Vertical">
<Label Margin="-80 0 0 0" HorizontalAlignment="Center">Doel kliniek</Label>
<ComboBox ItemsSource="{Binding Path=Klinieken}"
x:Name="doelKliniekDD"
SelectedItem="{Binding DoelKliniek}"
Margin="-120 0 0 0"
HorizontalAlignment="Center">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=KliniekNaam}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<StackPanel Orientation="Horizontal" Margin="20 0 0 0" Height="50" VerticalAlignment="Bottom" HorizontalAlignment="Center">
<Button Height="25" Style="{StaticResource Button_Orange}" Command="{Binding TransferButtonCommand}" CommandParameter="{Binding ElementName=window, Mode=OneWay}" Margin="0 0 10 0">Transfer</Button>
<Button Height="25" Style="{StaticResource Button_Orange}" Command="{Binding CopyButtonCommand}" CommandParameter="{Binding ElementName=window, Mode=OneWay}" Margin="10 0 20 0">Kopieer</Button>
<Button Height="25" Style="{StaticResource Button_Grey}" Command="{Binding CloseCommand}" CommandParameter="{Binding ElementName=window, Mode=OneWay}" Margin="20 0 0 0">Cancel</Button>
</StackPanel>
</StackPanel>
</Grid>
</Grid>
And here is the MainViewModel that is referenced in the .xaml:
//attributen
string monacoDirectory;
List<string> aanwezigeClinics;
Installation installation;
//constructor
public MainViewModel()
{
monacoDirectory = ConfigurationManager.AppSettings["monacoDirectory"];
aanwezigeClinics = new List<string>(Directory.GetDirectories(monacoDirectory, "*", SearchOption.TopDirectoryOnly));
aanwezigeClinics.Remove(monacoDirectory + #"\defaults");
aanwezigeClinics.Remove(monacoDirectory + #"\physics");
installation = new Installation(aanwezigeClinics);
Klinieken = new BindingList<Kliniek>();
foreach (var kliniek in installation.Klinieken)
{
Klinieken.Add(kliniek);
}
}
private Kliniek _doelKliniek;
private Kliniek _bronKliniek;
private Patient _selectedPatient;
private BindingList<Kliniek> _klinieken = new BindingList<Kliniek>();
public BindingList<Kliniek> Klinieken
{
get { return _klinieken; }
set { _klinieken = value; }
}
public Kliniek DoelKliniek
{
get { return _doelKliniek; }
set
{
_doelKliniek = value;
OnPropertyChanged();
}
}
public Kliniek BronKliniek
{
get { return _bronKliniek; }
set
{
_bronKliniek = value;
OnPropertyChanged();
}
}
public Patient SelectedPatient
{
get { return _selectedPatient; }
set
{
_selectedPatient = value;
MessageBox.Show("hoi");
OnPropertyChanged();
}
}
The data shows up in the gui just fine, which i was quite happy with. And using SelectedItem on the TabControl and the ComboBox i use later worked out beautifully aswell, but I just cant seem to get the SelectedItem on the ListView to work. I have added a button to test the output of the fields and SelectedPatient always returns as null.
If i check the 'Live Visual Tree' in VS and go to the properties of the ListView i can see the Patient as SelectedItem, so that tells me its not a selection but a binding problem. Furthermore ive tried to google for nested bindings, but the suggestions there didnt change anything about my situation.
Is a nested binding like this possible, or should i take a whole different approach?
Had a similar problem quite a while ago. The suggestions below solved it for me at the time.
Use SelectedValue instead of SelectedItem. This is because SelectedItem doesn't change until the control has been validated. SelectedValue changes whenever the user selects an item. The code you could use is as follows:
SelectedValue="{Binding BronKliniek, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
For the button part, try to provide CommandParameter="{Binding}" instead of CommandParameter="{Binding ElementName=window, Mode=OneWay}". This way, you'll get the value of the current DataContext of the button, which you can then use to extract the data you want from this DataContext.

Image Gallery with Flipview

I want to create a gallery image on the first page contains a thumbnail for that category and when thumbnail is selected, it will open the image and description of the selected image in flipview (can be swipe to the right and to the left when thumbnail is selected for the image before and after). I have difficulties when applying it into flipview.
Code:
MainPage XAML
<GridView
x:Name="itemGridView"
AutomationProperties.AutomationId="ItemGridView"
AutomationProperties.Name="Grouped Items"
Grid.RowSpan="3"
Padding="116,137,40,46"
ItemsSource="{Binding Source={StaticResource groupedItemsViewSource}}"
SelectionMode="None"
IsSwipeEnabled="false"
IsItemClickEnabled="True"
ItemClick="ItemView_ItemClick" Background="#FF6996D1" >
<GridView.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Left" Width="240" Height="180">
<Border>
<Image Source="{Binding ImagePath}" Stretch="Uniform" AutomationProperties.Name="{Binding Title}"/>
</Border>
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsWrapGrid GroupPadding="0,0,70,0"/>
</ItemsPanelTemplate>
</GridView.ItemsPanel>
</GridView>
MainPage:
public MainPage()
{
this.InitializeComponent();
Gallery();
}
private async void Gallery()
{
var sampleDataGroups = await DataItemSource.GetGroupsAsync();
this.DefaultViewModel["Groups"] = sampleDataGroups;
}
void ItemView_ItemClick(object sender, ItemClickEventArgs e)
{
// Navigate to the appropriate destination page, configuring the new page
// by passing required information as a navigation parameter
var itemId = ((SampleDataItem)e.ClickedItem).UniqueId;
this.Frame.Navigate(typeof(ItemDetailPage), itemId);
}
ItemDetailPage XAML:
<Grid Grid.Row="1" x:Name="contentRegion" Background="#FF6996D1">
<Image Source="{Binding ImagePath}" HorizontalAlignment="Left" Height="559" Margin="84,20,0,49" VerticalAlignment="Center" Width="732"/>
<ScrollViewer x:Name="myScroll" VerticalScrollBarVisibility="Auto" Margin="852,60,50,91" VerticalScrollMode="Auto" HorizontalScrollBarVisibility="Auto">
<TextBlock Text="{Binding Description}" TextWrapping="Wrap" Height="2210" Width="425" FontSize="27" TextAlignment="Justify" />
</ScrollViewer>
</Grid>
ItemDetailPage Code:
public ItemDetailPage()
{
this.InitializeComponent();
this.navigationHelper = new NavigationHelper(this);
this.navigationHelper.LoadState += navigationHelper_LoadState;
}
private async void navigationHelper_LoadState(object sender, LoadStateEventArgs e)
{
// TODO: Create an appropriate data model for your problem domain to replace the sample data
var item = await DataItemSource.GetItemAsync((String)e.NavigationParameter);
this.DefaultViewModel["Item"] = item;
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
navigationHelper.OnNavigatedTo(e);
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
navigationHelper.OnNavigatedFrom(e);
}
How do I apply flipview on ItemDetailPage?
Note:
For more code detail you can view the sample
To apply flipview on ItemDetailPage, we can add FlipView under "contentRegion" and set the Image and ScrollViewer as FlipView's ItemTemplate like following:
<Grid x:Name="contentRegion" Grid.Row="1" Background="#FF6996D1">
<FlipView ItemsSource="{Binding Group.Items}" SelectedItem="{Binding Item, Mode=TwoWay}">
<FlipView.ItemTemplate>
<DataTemplate>
<Grid>
<Image Width="732"
Height="559"
Margin="84,20,0,49"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Source="{Binding ImagePath}" />
<ScrollViewer x:Name="myScroll"
Margin="852,60,50,91"
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto"
VerticalScrollMode="Auto">
<TextBlock Width="425"
Height="2210"
FontSize="27"
Text="{Binding Description}"
TextAlignment="Justify"
TextWrapping="Wrap" />
</ScrollViewer>
</Grid>
</DataTemplate>
</FlipView.ItemTemplate>
</FlipView>
</Grid>
And in code-behind, set the data source like following:
private async void navigationHelper_LoadState(object sender, LoadStateEventArgs e)
{
// TODO: Create an appropriate data model for your problem domain to replace the sample data
var item = await DataItemSource.GetItemAsync((String)e.NavigationParameter);
var group = await DataItemSource.GetGroupByItemAsync(item);
this.DefaultViewModel["Group"] = group;
this.DefaultViewModel["Item"] = item;
}
Here I add a GetGroupByItemAsync(SampleDataItem item) method in DataItemSource which can retrieve the group according to the item.
public static async Task<SampleDataGroup> GetGroupByItemAsync(SampleDataItem item)
{
await _DataItemSource.GetSampleDataAsync();
// Simple linear search is acceptable for small data sets
var matches = _DataItemSource.Groups.Where(group => group.Items.Contains(item));
if (matches.Count() == 1) return matches.First();
return null;
}
Besides these, we also need to remove DataContext="{Binding Item}" form root Grid and put in in <Grid Background="#FF6996D1" DataContext="{Binding Item}">.
After this, the FlipView should be able to work. However here is a strange behavior, if we select the second or third image, the previous image in flip view won't show like following
We are investigating on this issue. As a workaround, we can disable FlipView's virtualization by changing its ItemsPanel like:
<FlipView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</FlipView.ItemsPanel>
The complete XAML code of ItemDetailPage might like:
<Page x:Class="ImageGalerry.ItemDetailPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:common="using:ImageGalerry.Common"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:data="using:ImageGalerry.Data"
xmlns:local="using:ImageGalerry"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
DataContext="{Binding DefaultViewModel,
RelativeSource={RelativeSource Self}}"
mc:Ignorable="d">
<Grid d:DataContext="{Binding Groups[0].Items[0], Source={d:DesignData Source=/DataModel/DataItem.json, Type=data:DataItemSource}}" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="140" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<!-- Back button and page title -->
<Grid Background="#FF6996D1" DataContext="{Binding Item}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1366" />
</Grid.ColumnDefinitions>
<Button x:Name="backButton"
Margin="39,59,0,0"
VerticalAlignment="Top"
AutomationProperties.AutomationId="BackButton"
AutomationProperties.ItemType="Navigation Button"
AutomationProperties.Name="Back"
Command="{Binding NavigationHelper.GoBackCommand,
ElementName=pageRoot}"
Style="{StaticResource NavigationBackButtonNormalStyle}" />
<TextBlock x:Name="pageTitle"
Margin="120,40,30,40"
VerticalAlignment="Top"
IsHitTestVisible="false"
Style="{StaticResource HeaderTextBlockStyle}"
Text="{Binding Title}"
TextWrapping="NoWrap" />
<!--<MediaElement x:Name="mediaplayer" Source="images/ost.mp3" AudioCategory="BackgroundCapableMedia" />
<Button x:Name="PlayButton" Content="Stop" Click="PlayButton_Click" Margin="1274,72,0,30" />-->
</Grid>
<Grid x:Name="contentRegion" Grid.Row="1" Background="#FF6996D1">
<FlipView ItemsSource="{Binding Group.Items}" SelectedItem="{Binding Item, Mode=TwoWay}">
<FlipView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</FlipView.ItemsPanel>
<FlipView.ItemTemplate>
<DataTemplate>
<Grid>
<Image Width="732"
Height="559"
Margin="84,20,0,49"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Source="{Binding ImagePath}" />
<ScrollViewer x:Name="myScroll"
Margin="852,60,50,91"
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto"
VerticalScrollMode="Auto">
<TextBlock Width="425"
Height="2210"
FontSize="27"
Text="{Binding Description}"
TextAlignment="Justify"
TextWrapping="Wrap" />
</ScrollViewer>
</Grid>
</DataTemplate>
</FlipView.ItemTemplate>
</FlipView>
</Grid>
</Grid>
</Page>
Please note that disabling UI virtualization for FlipView may negatively impact performance especially when there are a lot of images. If you have a lot of images, you can try to use incremental loading and data virtualization.

Why is Command not invoked when binding value is null?

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!

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.

How to bind data correctly to a WPF button using MVVM

Here is my setup, it seems I am not binding the data correctly. The outcome should be a Button with an Image and text displayed. Nothing is displayed.
<UserControl x:Class="AmebaPrototype.UI.LandingPivot.featureControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="330" d:DesignWidth="960"
DataContext="{Binding Source={StaticResource viewModelLocator}, Path=VideoViewModel}"
>
<UserControl.Resources >
<DataTemplate x:Key="custTemplate" >
<StackPanel >
<Image Source="{Binding ImagePath}"/>
<TextBlock Text="{Binding MyTitle}"/>
</StackPanel>
</DataTemplate>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="{x:Null}" >
<Button Content="Button" Height="316"
HorizontalAlignment="Left" Margin="12,2,0,0"
Name="button1" VerticalAlignment="Top"
Width="423" Click="button1_Click"
ContentTemplate="{DynamicResource custTemplate}"
/>
<Button Content="Button" Height="156" HorizontalAlignment="Left" Margin="441,2,0,0" Name="button2" VerticalAlignment="Top" Width="208" Click="button2_Click" />
<Button Content="Button" Height="156" HorizontalAlignment="Left" Margin="669,2,0,0" Name="button3" VerticalAlignment="Top" Width="208" />
<Button Content="Button" Height="156" HorizontalAlignment="Left" Margin="441,162,0,0" Name="button4" VerticalAlignment="Top" Width="208" />
<Button Content="Button" Height="156" HorizontalAlignment="Left" Margin="669,162,0,0" Name="button5" VerticalAlignment="Top" Width="208" />
</Grid>
Model:
public class Video
{
public string MyTitle { get; set; }
public string ImagePath { get; set; }
}
ViewModel
public ViewModel VideoViewModel
{
get
{
if(viewmodel == null)
{
viewmodel = new ViewModel();
viewmodel.ListData.Clear();
viewmodel.ListData.Add(new Video { MyTitle = "Title1", ImagePath = "exampleimage.com/image.png" });
viewmodel.ListData.Add(new Video { MyTitle = "Title2", ImagePath = "exampleimage.com/image.png" });
}
return viewmodel;
}
}
If you really want to do this using a DataTemplate, then:
<Button>
<Button.Content>
<x:Type TypeName="Visual"/>
</Button.Content>
<Button.Template>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<ContentPresenter ContentSource="Content"/>
</Grid>
</ControlTemplate>
</Button.Template>
<Button.ContentTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Name}"/>
</DataTemplate>
</Button.ContentTemplate>
</Button>
Your ViewModel binding should be okay to use here so in the DataTemplate you can just tweak it to include your Image and TextBlock.
DataTemplate in resources:
<DataTemplate x:Key="bTemplate">
<Button Label="{Binding MyTitle}"
Command="{Binding Execute}">
<Image Source="{Binding ImagePath}" />
</Button>
</DataTemplate>
Not exactly as in your question but then you can use a control that takes an ItemSource such as a ListView:
<ListView
ItemsSource="{Binding ListData}"
ItemTemplate="{StaticResource bTemplate}"
>
</ListView>
I added a Command , since it is necessary if you want to implement MVVM rather than event click.

Categories