WPF hyperlink in datatemplate listboxitem - wpf

I have a data template in WPF that is attached to a ResultsViewModel class and renders the VM in a table format. A bunch of these make up ListBoxItems in a ListBox. What I want is in each individual table to have a little X at the top right of the border where if you click it, it calls a function that removes that item from the listbox.
I have tried with a Hyperlink and event OnClick but then I have to have the DataTemplate in the main XAML and not in a resource dictionary as it needs a x:Class tag to use events, but then the event gets fired in the MainViewModel, which isn't the worst thing in the world as the observable list is held in the MainViewModel and needs to be removed at that point anyway, but I can't figure out how to get a reference to the ResultsViewModel of the list box item that contained the data template that was clicked
<DataTemplate x:Key="ErroredResultsTemplate" DataType="x:Type vm:ResultsViewModel" >
<Border x:Name="Border" BorderBrush="{StaticResource ResultProcessedBorder}" Background="{StaticResource ResultFill}" BorderThickness="4" CornerRadius="10" Margin="6" Padding="5" Width="110" Height="110">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="20" />
<RowDefinition Height="83" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Foreground="{StaticResource ResultGrayText}" FontWeight="Bold" HorizontalAlignment="Right" VerticalAlignment="Top">
<Hyperlink Click="Close_Results">X</Hyperlink>
</TextBlock>
<TextBlock Width="90" Text="An error occurred calculating results" TextWrapping="Wrap" Foreground="{StaticResource ResultGrayText}" FontWeight="Bold" Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Top" TextAlignment="Center" />
</Grid>
</Border>
</DataTemplate>

You can achieve this in two ways:
Create a property of type ResultsViewModel in your parent view model (that contains your collection of ResultsViewModel objects) and bind that to the SelectedItem property of your ListBox. Add some kind of RelayCommand to the parent view model to handle the delete action, add a Button to your DataTemplate and bind its Command property to the new command. Then, when any delete button is clicked, you can just remove the item found in the SelectedItem property from your collection and the UI should update accordingly (assuming that you've implemented the INotifyPropertyChange interface).
You can simply bind from the DataTemplate of each item in the ListBox to the parent view model directly. This assumes that you have a Command in your parent view model named Delete and that the parent view model is bound to the DataContext property of the Window or UserControl that the ListBox appears in. Also note the important CommandParameter="{Binding}" part which passes the data object from each item in the collection to the object parameter in the Command when a Command is called.
Example:
<Button Content="X" Command="{Binding DataContext.Delete,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type
XmlNameSpace:WindowOrUserControlName}}, Mode=OneWay}"
CommandParameter="{Binding}" />

Related

using a datatemplate for a list of listviewitems

I've created a listview. The listview's source is set to a list of "listviewitem"s. The listviewItem's content is set to the specific class that I need data from.
Somehow, the datatemplate from my xaml document is not recognised which results in a default template instead of the one I defined.
My template looks like this:
<DataTemplate x:Key="Tiled">
<StackPanel Height="100" Width="90">
<Grid Width="70" Height="70" HorizontalAlignment="Center">
<Image Source="{Binding Path=Content.Icon}" Margin="6,6,6,9"/>
</Grid>
<TextBlock Style="{Binding Path=Content.Name}" FontSize="13"
HorizontalAlignment="Center" Margin="0,0,0,1" />
</StackPanel>
</DataTemplate>
I know that I can also set the source of the list to a collection of objects instead of listviewitems, but I need the contextmenu from the listviewitems.
I know that I can also set the source of the list to a collection of objects instead of listviewitems, but I need the contextmenu from the listviewitems.
No you don't, that's what the ItemContainerStyle is for.
What you by the way should see in the output window:
System.Windows.Data Error: 26 : ItemTemplate and ItemTemplateSelector are ignored for items already of the ItemsControl's container type; Type='ListBoxItem'

Binding the content of a ListBoxItem to something not related to

I'm new to WPF, but am pretty familiar with binding list box controls to observable collections in the view model.
In my current project we have a ListBox that is used for navigating to different pages in a frame box. I want to add some display information to the first ListBoxItem to show which object (in this case, the Scenario) is being worked on (it is selected in a previous frame that visible in the subsequent frames). The ListBox itself is using a static list defined in the xaml, so it isn't bound to anything in the ViewModel. The CurrentScenario is a property on the ViewModel. I was able to add a Label to the same window that contains this ListBox and successfully bind CurrentScenario.Id to its content, and it updated correctly, so I know that the path in the Binding statement should resolve correctly.
<ListBox
Style="{StaticResource FunctionBackground}"
IsSynchronizedWithCurrentItem="True"
>
<ListBoxItem Style="{StaticResource FunctionListBoxItemStyle}">
<ListBoxItem.ContentTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="Scenario" />
<TextBlock Grid.Row="1" Text="{Binding Path=CurrentScenario.Id}"/>
</Grid>
</DataTemplate>
</ListBoxItem.ContentTemplate>
</ListBoxItem>
<ListBoxItem Style="{StaticResource FunctionListBoxItemStyle}" >Parameter</ListBoxItem>
<ListBoxItem Style="{StaticResource FunctionListBoxItemStyle}" >Run</ListBoxItem>
<ListBoxItem Style="{StaticResource FunctionListBoxItemStyle}" >Results</ListBoxItem>
</ListBox>
When I try to add this extra information to the listbox item, from what I can tell, the list box item has an empty text block below the text block with the word "Scenario." I can't figure out why the empty text box content is not showing the value of the bound property. When I put a normal string in the Text property of the second text block, it shows up correctly.
I imagine that either ListBoxItem content is only set up be bound to properties related to the ItemSource, and so it ignores attempts to bind to other things, or maybe there is something fundamental in WPF that I am missing. Or both...
Thanks if anyone has any ideas!
So if property CurrentScenario is in ViewModel you can use RelativeSource to binding to this property.
...
<TextBlock Grid.Row="0" Text="Scenario" />
<TextBlock Grid.Row="1" Text="{Binding Path=DataContext.CurrentScenario.Id, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}"/>
...

Button and ToggleButton in wpf Toolbar using MVVM

I have a wpf Toolbar in a ToolbarTray inside my application which must host Buttons and ToggleButtons.
Can someone suggest me how to implement this behavior in MVVM?
The code below is what I have right now:
<ToolBarTray Margin="5,30,5,30" MinWidth="35" HorizontalAlignment="Center" Orientation="Vertical" Background="Transparent">
<ToolBar x:Name="ToolBarControl" HorizontalAlignment="Stretch" ItemsSource="{Binding ToolBarItems}" >
<ToolBar.ItemTemplate>
<DataTemplate>
<Button Style="{StaticResource {x:Static ToolBar.ButtonStyleKey}}" HorizontalAlignment="Stretch" Margin="0,0,0,15"
Template="{Binding ToolBarIcon}"
cal:Message.Attach="[Event Click] = [Action Select()]"
IsEnabled="True"
ToolTip="{Binding Text}"/>
</DataTemplate>
</ToolBar.ItemTemplate>
</ToolBar>
</ToolBarTray>
Where the Button could be a normal Button or a ToggleButton.
Thanks in Advance.
In MVVM pattern, your model class will contain the properties (all data objects) which you want to bind and display in view. So ToolBarItems collection will be part of model.
Your view basically will contain the above code you have written. And in code behind file, there will be an object of type model class as a property.
Your viewmodel can initialize the model and view objects and bind the model to view's datacontext.

WPF Listbox & MVVM binding

I am following MVVM in C# and am attempting to display a view in a listbox.
I am setting the listbox itemsource (in code, not in binding and using the viewmodels collection) and then set the datatemplate to be my view in xaml. The issue I'm encountering is my view always loads with its default constructor values, if I remove the datacontext from the view however it loads fine.
Below is the listbox I am creating in xaml
<ListBox Name="lbCatalogues" HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<view:CatalogueRowView/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
This is the xaml for my view. If I remove the DataContext it works
<UserControl.DataContext>
<model:CatalogueModel />
</UserControl.DataContext>
<Grid Name="Container" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50" />
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="40" />
</Grid.ColumnDefinitions>
<!-- Catalogue_ID, UploadedTime, Client_ID, Name, Desc, Filesize -->
<Label Name="lblCatalogueID" Content="{Binding Path=CatalogueID}" Grid.Column="0"/>
<Label Name="lblUploadedTime" Content="{Binding Path=UploadedTime}" Grid.Column="1"/>
<Label Name="lblCatalogueName" Content="{Binding Path=Name}" Grid.Column="2"/>
<Label Name="lblCatalogueDescription" Content="{Binding Path=Description}" Grid.Column="3"/>
<Label Name="lblFilesize" Content="{Binding Path=Filesize}" Grid.Column="4"/>
<Grid/>
This is the code where I am setting the listbox ItemSource:
lbCatalogues.ItemsSource = catalogueViewModel.Records;
My question is how can I get the view to load correctly within the listbox so that each item in the listbox has a DataContext linked to that listbox Itemsource?
You already know the answer: simply remove <UserControl.DataContext> from your UserControl
You are telling your UserControl to use a new instance of CatelogueModel for the DataContext, and this overrides any DataContext that is set when you use your UserControl. See MSDN's list of Dependency Property Precedence for more info
I do not ever recommend setting the DataContext inside a UserControl. It goes against how WPF is meant to work by having separate UI and data layers, and is a problem for anyone trying to use your UserControl
As for your question about each item in the ListBox linking to the ItemsSource, DataTemplates simply tell WPF how to draw an object. The data behind the object still remains.
For example, your ListBox contains a list of Record objects, and your DataTemplate is telling the ListBox to draw each one of those records with CatelogueRowView. The actual data behind the CatelogRowView is still your data object from catelogueViewModel.Records
lbCatalogues.ItemsSource = catalogueViewModel.Records; instead of this Simply bind ItemsSource of ListBox as ItemsSource="{Binding Records}" in xaml. I hope this will help.

How to set a data context for listbox item in listbox item template different from the one set as itemsource in listbox

<UserControl.Resources>
<DataTemplate x:Key="LstBoxTemplate">
<TextBlock Text="{Binding Item}" TextWrapping="Wrap" HorizontalAlignment="Left"/>
<Image Grid.Column="2" Margin="0,0,10,0" Visibility="{Binding isVisible,Converter={StaticResource ImageCtlVisibilityConverter}}" Source="/pjct;component/Images/im.png"/>
</DataTemplate>
</UserControl.Resources>
<ListBox x:Name=lstbox ItemsSource="{Binding itemList}" ItemTemplate="{StaticResource LstBoxTemplate}" />
You question needs more detail, and so I may be missing the point...
If your listbox is bound to a collection of custom objects that you control (aka a view model). Then it should be fairly straight forward for you to add a property to the view model that contains the object that you want to bind your listboxitem to...

Resources