I want to write the event for list box from View Model. I try like this:-
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Gray" Padding="5" BorderThickness="1">
<StackPanel Orientation="Horizontal">
<Border BorderBrush="Wheat" BorderThickness="1">
<Image Name="ListPersonImage" Source="{Binding PersonImage}" Height="100" Width="100" Stretch="Uniform" Margin="10,0,0,0"/>
</Border>
<TextBlock Text="{Binding FirstName}" Name="firstName" Width="200" Foreground="White" Margin="10,10,0,0" FontWeight="SemiBold" FontSize="22" />
<Button DataContext="{Binding DataContext, ElementName=listBox1}" Command="{Binding addPerson}" Height="80" Width="80" >
<Button.Background>
<ImageBrush ImageSource="{Binding imagePath, Converter={StaticResource pathToImageConverter}}" Stretch="Fill" />
</Button.Background>
</Button>
</StackPanel>
</Border>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Tap">
<i:InvokeCommandAction Command="{Binding ItemSelectedCommand,Mode=OneWay}" CommandParameter="{Binding}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
My ViewModel:-
public RelayCommand<MVVMListBoxModel> ItemSelectedCommand { get; private set; }
public MVVMListBoxViewModel()
{
ItemSelectedCommand = new RelayCommand<MVVMListBoxModel>(ItemSelected);
}
private void ItemSelected(MVVMListBoxModel myItem)
{
MessageBox.Show("Name==>" + myItem.FirstName);
//throw new NotImplementedException();
}
But nothing happening. Please let me know where I did mistake.
Thanks in advance.
Check output window to see if you got binding error. It seems that you got one, because you have ItemSelectedCommand defined in MVVMListBoxViewModel but ListBoxItem's DataContext is corresponding MVVMListBoxModel, so binding engine couldn't find the command.
Try to move definition of ItemSelectedCommand to MVVMListBoxModel and see if message box get displayed this way.
Do you want the trigger to be upon the SelectionChanged of the Listbox Item? Then the trigger should be outside the <ListBox.ItemTemplate> ... </ListBox.ItemTemplate>.
And the trigger should bind the CommandParamter to the SelectedItem
<i:Interaction.Triggers>
<i:EventTrigger EventName="Tap">
<i:InvokeCommandAction Command="{Binding ItemSelectedCommand,Mode=OneWay}" CommandParameter="{Binding SelectedItem}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
Related
I'm having trouble with binding a ListBox and I can't for the life of me figure out what's wrong. Most of the time it works fine, but in a special test configuration I have for my application it doesn't work and it's driving me crazy. I've literally stared at it for hours and tried all sorts of things. It's gotta be a binding issue, but I can't find it. There aren't any exceptions or binding errors being thrown that I can see.
The test configuration is identical to my debug configuration, but with an additional compilation symbol. It allows me to test the WPF application without running the entire software stack. It's an "offline mode" for all intents and purposes.
<ribbon:Ribbon Height="45">
<ribbon:Ribbon.ApplicationMenu>
<ribbon:RibbonApplicationMenu Width="56">
<ribbon:RibbonApplicationMenuItem Header="New Campaign" Command="{Binding Path=AddCampaign}" ImageSource="images\new.png"/>
<ribbon:RibbonApplicationMenuItem Header="Save Campaigns" Command="{Binding Path=Save}" ImageSource="images\save.png" />
<ribbon:RibbonApplicationMenuItem Header="Import Campaign" Command="{Binding Path=ImportCampaign}" ImageSource="images\campaign-import-128x128.png"/>
<ribbon:RibbonApplicationMenuItem Header="Export Campaign" Command="{Binding Path=ExportCampaign}" ImageSource="images\campaign-export-128x128.png" />
<ribbon:RibbonApplicationMenuItem Header="Terms of Use" Command="{Binding Path=Eula}" ImageSource="images\terms.png"/>
<ribbon:RibbonApplicationMenuItem Header="Resources" Command="{Binding Path=Resources}" ImageSource="images\resource.jpg"/>
<ribbon:RibbonApplicationMenuItem Header="About" Command="{Binding Path=About}" ImageSource="images\resource.jpg"/>
<ribbon:RibbonApplicationMenu.AuxiliaryPaneContent>
<StackPanel>
<StackPanel Orientation="Horizontal">
<Label Width="130" Content="Campaign"/>
<Label Width="60" Content="Duplicate"/>
<Label Width="50" Content="Delete"/>
</StackPanel>
<ListBox ItemsSource="{Binding Path=Repository.Campaigns}"
SelectedItem="{Binding Path=SelectedCampaign, Mode=TwoWay}"
ItemContainerStyle="{StaticResource ListBoxItemStyle}"
Height="212"
ScrollViewer.VerticalScrollBarVisibility="Visible"
ScrollViewer.HorizontalScrollBarVisibility="Hidden">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Label Content="{Binding Path=Name}" Width="130" />
<Button Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ItemsControl}}, Path=DataContext.DuplicateCampaign}"
CommandParameter="{Binding Path=Name}" Width="60">
<Button.Template>
<ControlTemplate>
<Border HorizontalAlignment="Center" VerticalAlignment="Center" >
<Image Source="/asdf;component/images/duplicate.png" Width="24" Height="24"/>
</Border>
</ControlTemplate>
</Button.Template>
</Button>
<Button Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ItemsControl}}, Path=DataContext.RemoveCampaign}"
CommandParameter="{Binding Path=Name}" Width="50">
<Button.Template>
<ControlTemplate>
<Border HorizontalAlignment="Center" VerticalAlignment="Center" >
<Image Source="/asdf;component/images/delete.png" Width="24" Height="24"/>
</Border>
</ControlTemplate>
</Button.Template>
</Button>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Content="View Archived"
Command="{Binding Path=ArchivedCampaigns}"></Button>
</StackPanel>
</ribbon:RibbonApplicationMenu.AuxiliaryPaneContent>
<ribbon:RibbonApplicationMenu.FooterPaneContent>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<ribbon:RibbonButton Label="LOGOUT" Command="{Binding Path=Logout}" />
<ribbon:RibbonButton Label="EXIT" Command="{Binding Path=Exit}" />
</StackPanel>
</ribbon:RibbonApplicationMenu.FooterPaneContent>
</ribbon:RibbonApplicationMenu>
</ribbon:Ribbon.ApplicationMenu>
Here's my login method where the window is created and the DataContext is bound. UITEST is my offline mode configuration.
private void Login()
{
MainVM = new ViewModel.MainWindowVM();
_mainWindow = new RibbonMainWindow
{
DataContext = MainVM
};
//Prompt user for login.
#if UITEST
_loggedIn = true;
MainVM.LoadData();
#else
var loginWindow = new Login();
var login = new ViewModel.LoginVM();
loginWindow.DataContext = login;
loginWindow.ShowDialog();
if (loginWindow.DialogResult != null) _loggedIn = loginWindow.DialogResult.Value;
#endif
if (_loggedIn && MainVM.Loaded)
{
_mainWindow.ShowDialog();
}
}
Edit: As near as I can guess, it might be due to using the legacy RibbonControlsLibrary from the pre .NET 4.5 days. I retargeted the software to .NET 4.6.1 last year so I switched to the Ribbon built-in to .NET and now it works oddly enough. If I try to mess with anything else in the window it just seems to break again. Like if I try to strongly-type the DataContext in the window.
As near as I can guess, it might be due to using the legacy RibbonControlsLibrary from the pre .NET 4.5 days. I retargeted the software to .NET 4.6.1 last year so I switched to the Ribbon built-in to .NET and now it works oddly enough. If I try to mess with anything else in the window it just seems to break again. Like if I try to strongly-type the DataContext in the window.
I have the following code. No checked/ unchecked event is fired when PlayVideoState value is set to true/false. Does anything missing? please suggest.
<CheckBox Style="{DynamicResource PlayButton}" Margin="6" Name="PlayButton" IsChecked="{Binding _Extensions.PlayVideoState, Mode=TwoWay}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Checked">
<ei1:GoToStateAction StateName="VideoOn"/>
</i:EventTrigger>
<i:EventTrigger EventName="Unchecked">
<ei1:GoToStateAction StateName="VideoOff"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<TextBlock x:Name="textBlock2" FontFamily="{DynamicResource FontFamily1}" Foreground="{DynamicResource Text01}" Margin="0" VerticalAlignment="Center" FontSize="21.333"><Run Language="en-gb" Text="Video"/></TextBlock>
</CheckBox>
i try to bind the TextBox's text in MenuItem's header to MenuItem's Tag property.
but it won't work correct, always get Null in Tag property.
the code is like below...
<Button x:Name="Button1" Content="Test" HorizontalAlignment="Left" Width="182" Height="34" VerticalAlignment="Top" Margin="160,113,0,0">
<Button.ContextMenu>
<ContextMenu PlacementTarget="{Binding ElementName=Button1}" Placement="Bottom">
<MenuItem Tag="{Binding ElementName=TextBox1, Path=Text}" Click="MessageBox_ShowTag">
<MenuItem.Header>
<Grid Height="25" MinWidth="153">
<Label Content="Label1" Width="86" HorizontalAlignment="Left" VerticalContentAlignment="Center"/>
<TextBox x:Name="TextBox1" VerticalContentAlignment="Center" Margin="91,0,0,0"/>
</Grid>
</MenuItem.Header>
</MenuItem>
</ContextMenu>
</Button.ContextMenu>
</Button>
When click on menuitem, call the MessageBox to show the tag in MenuItem
( MessageBox.Show( ( sender as MenuItem ).Tag?.ToString() ); )
MessageBox has show but content is always empty.
Result:
how can i bind to textbox?
I don't know why you have to bind it like that. An alternative would be to bind to a property (here: MyText) that implements INotifyPropertyChanged, and then pass the DataContext to the menu like this:
<Button x:Name="Button1" Content="Test" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Button.ContextMenu>
<ContextMenu Placement="Bottom" DataContext="{Binding Path=PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
<MenuItem>
<MenuItem.Header>
<Grid Height="25" MinWidth="153">
<Label Content="Label1" Width="86" HorizontalAlignment="Left" VerticalContentAlignment="Center"/>
<TextBox Name="TextBox1" Text="{Binding Path=MyText}"
VerticalContentAlignment="Center" Margin="91,0,0,0"/>
</Grid>
</MenuItem.Header>
</MenuItem>
</ContextMenu>
</Button.ContextMenu>
</Button>
And then not relay on Click event to get the TextBox value at all.
If you want to know why your binding doesn't work, its because your MenuItem can't find an object named TextBox1 in its Namescope. If you must (namescopes are a bit tricky), you may make it work by creating a new Namescope for your MenuItem and registering the name for the TextBox:
NameScope.SetNameScope(mi1, new NameScope());
mi1.RegisterName("TextBox1", TextBox1);
where mi1 is the name of your MenuItem, then your binding will work:
<Button x:Name="Button1" Content="Test" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Button.ContextMenu>
<ContextMenu Placement="Bottom">
<MenuItem Tag="{Binding ElementName=TextBox1, Path=Text}" Click="MessageBox_ShowTag" Name="mi1">
<MenuItem.Header>
<Grid Height="25" MinWidth="153">
<Label Content="Label1" Width="86" HorizontalAlignment="Left" VerticalContentAlignment="Center"/>
<TextBox Name="TextBox1" VerticalContentAlignment="Center" Margin="91,0,0,0"/>
</Grid>
</MenuItem.Header>
</MenuItem>
</ContextMenu>
</Button.ContextMenu>
</Button>
Also, avoid x:Name whenever you can. It can cause some nasty memory leaks. Use Name instead.
Like mentioned in the comment also, there could be better solution to actual problem you are trying to solve.
But anyhow if you want solution for your specific problem it can be solved by using x:Reference in place of ElementName like this:
<MenuItem Tag="{Binding Source={x:Reference TextBox1}, Path=Text}"/>
ElementName is not working because VisualTree is different. In case interested read further here - ElementName v/s x:Reference.
Try binding via RelativeSource:
<TextBox x:Name="TextBox1" Text="{Binding Path=Tag, RelativeSource={RelativeSource AncestorType={x:Type MenuItem}}}" VerticalContentAlignment="Center" Margin="91,0,0,0"/>
I have WPF MVVM application and I am currently trying to use a Checkbox that is bound to a column in a list that it is bound to. I have a EventTriggers set and the command bound to the VM. Everything fires great....Except I do NOT want the events to fire when populated from the list, ONLY when the user checks or unchecks the checkbox. See Code:
<StackPanel Orientation="Vertical" Background="Transparent" DockPanel.Dock="Right" Margin="2,0" VerticalAlignment="Center">
<Label Content="Active" />
<CheckBox x:Name="CbArchiveAoi" VerticalAlignment="Center" HorizontalAlignment="Center" FlowDirection="RightToLeft" IsChecked="{Binding Path=PatientAoi.AoiIsActive}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Checked">
<i:InvokeCommandAction Command="{Binding ArchiveAoiCommand, Mode=OneWay}"
CommandParameter="{Binding ElementName=CbArchiveAoi}"/>
</i:EventTrigger>
<i:EventTrigger EventName="Unchecked">
<i:InvokeCommandAction Command="{Binding ArchiveAoiCommand, Mode=OneWay}" CommandParameter="{Binding ElementName=CbArchiveAoi}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</CheckBox>
</StackPanel>
public ICommand ArchiveAoiCommand
{
get { return new RelayCommand<object>(ArchiveAoiExecute, AlwaysTrueCanExecute); }
}
private void ArchiveAoiExecute(object obj)
{
string dddd = obj.ToString();
}
I'd remove the Interaction.Triggers and use the CheckBox.CommandandCommandParameter properties instead.
<CheckBox x:Name="CbArchiveAoi"
VerticalAlignment="Center"
<-- snip -->
Command="{Binding ArchiveAoiCommand, Mode=OneWay}"
CommandParameter="{Binding ElementName=CbArchiveAoi}" />
I have the following user control
<ListBox ItemsSource="{Binding Persons}"
SelectedItem="{Binding SelectedPerson}"
VerticalAlignment="Top" Width="166" >
<ListBox.Template>
<ControlTemplate>
<StackPanel >
<ItemsPresenter/>
<Button Content="Add" Background="Transparent" Command="{Binding NewItemCommand}"/>
</StackPanel>
</ControlTemplate>
</ListBox.Template>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Button Height="16" Width="16" Background="Transparent" Command="{Binding DeleteItemCommand}">
<Image Source="images/delete-icon.png" />
</Button>
<TextBlock Text="{Binding Name}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
and i have a view model for it with two commands, the first command that you can see above NewItemCommand is working fine, how ever the second command DeleteItemCommand doesn't function.
is it because its in the item template?
Yes, this is because the DataContext for the ItemTemplate is the Item from Persons not the ViewModel
To bind the DeleteItemCommand on each item you will need to bind back to the ViewModel that is holding the command
Example, binding to the DataContext of the ListBox
<Button Command="{Binding Path=DataContext.DeleteItemCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}}" />
Edit:
If you want to remove the item that the button was clicked on you can pass the item the button belongs to as the CommandParameter and handle that in your comman, Not sure what type of command you are using but this is simple if you are using RelayCommand or DelegateCommand
<Button Command="{Binding Path=DataContext.DeleteItemCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}}"
CommandParameter="{Binding}" />
public MainWindow()
{
InitializeComponent();
DeleteItemCommand = new RelayCommand(person => DeletePerson(person as Person));
}
private void DeletePerson(Person person)
{
Collection.Remove(person);
}