Accessing ItemsSource source item - c#

I'm creating an error list control similar to the in Visual Studio. Each error is represented by a class with three values: type (enum: Error/Warning/Message), text (string) and time (DateTime). The class has also two more read only getters: TimeString (returns time as HH:MM) and Icon (returns icon path based on type).
I have an ItemsControl bound to an ObservableCollection of objects via ItemsSource property.
I now want to implement a context menu for each of the items with two actions: Copy to clipboard and Delete from list.
How can I access the original item from the collection from the context menu item click handler?
Here is my XAML code:
<ItemsControl Name="itemsControl" ItemsSource="{Binding Items, ElementName=ConsoleWindow}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="Console.Items">
<Border Name="itemBorder" BorderBrush="LightGray" BorderThickness="0,0,0,1" SnapsToDevicePixels="True" Padding="4">
<Border.ContextMenu>
<ContextMenu>
<MenuItem Header="Copy to clipboard" />
<MenuItem Header="Delete" />
</ContextMenu>
</Border.ContextMenu>
<DockPanel>
<Image Width="16" Height="16" Source="{Binding Icon}" Margin="0,3,4,0" VerticalAlignment="Top" DockPanel.Dock="Left" />
<TextBlock VerticalAlignment="Center" TextWrapping="Wrap" DockPanel.Dock="Left">
<Run Text="{Binding Text}" />
<TextBlock Foreground="Gray" FontSize="9">
<Run Text=" (" /><Run Text="{Binding TimeString, Mode=OneWay}" /><Run Text=") " />
</TextBlock>
</TextBlock>
</DockPanel>
Thanks for any help

The DataContext property of any of the FrameworkElement derived elements (i.e. the TextBlock or Image or MenuItem) in the DataTemplate should have the original data item (the child automatically inherits the datasource of its parent unless otherwise set).
As part of the click event handler you get the element that is the source of the event, so cast it to MenuItem and check its DataContext property.

#slugster's answer would work. A more WPF-esque way of doing this would be to use a command for each menu item and set the parameter to {Binding}. WPF comes with commands for copy and possibly delete, so you might reuse those.

Related

How to collapse contents of a databound ListBoxItem when a button is clicked in WPF MVVM

I have a ListBox in my WPF MVVM app using the following code:
<GroupBox Grid.Column="0" Margin="0,0,0,-58">
<DockPanel>
<TextBlock DockPanel.Dock="Top"
HorizontalAlignment="Center"
Margin="0,0,0,8"
FontWeight="Bold"
Text="{x:Static p:Resources.AvaliableLEDsLabel}" />
<ListBox Name="AvailableLEDsListbox" SelectionMode="Extended"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.DropHandler="{Binding}"
ItemTemplate="{StaticResource DataTemplateListBoxItem}"
ItemsSource="{Binding AvailableLeds}"
ScrollViewer.VerticalScrollBarVisibility="Hidden"
>
<ListBox.GroupStyle>
<StaticResource ResourceKey="StyleListBoxGroup" />
</ListBox.GroupStyle>
</ListBox>
</DockPanel>
</GroupBox>
This displays grouped lists of devices, with LEDs under them. The DataTemplate is the following:
<GroupStyle x:Key="StyleListBoxGroup">
<GroupStyle.HeaderTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Button Command="{Binding HideGroupCommand}">X</Button>
<TextBlock VerticalAlignment="Center"
HorizontalAlignment="Left"
FontWeight="Bold"
Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
<DataTemplate x:Key="DataTemplateListBoxItem">
<TextBlock x:Name="LedId" Text="{Binding LedId}"/>
</DataTemplate>
I would like to make the X button in the header hooked up to the HideGroupCommand toggle the hiding of all the items under that particular header. How would I go about doing this? Thanks in advance.
You have few options.
First one :
You would need to have a property in your view model something like 'ListBoxVisibility' then u would bind that property to your UI. In command text u just changed visibility property of that property in view model- so u have it reflected on UI. This visibility property can be of type 'bool' , or 'Visibility' or whatever. Only if it's type of Visibility u don't need converter when binding.
NOTE : Some people use it - even though it kinda goes out of general principel of MVVM patter. But sometimes u have to do it.
Second
If wanna stick to MVVM , then u need to fully separate your UI from your viewmodel. Create click event and change visibility.

ProgressBar into ListBoxItem becomes unselectable while performing steps in XAML

I have ListBox dynamically generated. Each item contains a ProgressBar which I can stop, pause or resume by the means of clicking the relative voice on its ContextMenu.
Here is the XAML code:
<ListBox Grid.Column="1" Name="TransfersList" Margin="30,10,-0.444,34.889" ItemsSource="{Binding DataTx}"
SelectionChanged="TransfersList_SelectionChanged" Grid.Row="1" Grid.ColumnSpan="3"
HorizontalContentAlignment="Stretch">
<ListBox.ContextMenu>
<ContextMenu>
<MenuItem Header="Ottieni informazioni" Click="GetInfo" />
<MenuItem Header="Metti in pausa" Click="PauseTransfer" />
<MenuItem Header="Riprendi trasferimento" Click="ResumeTransfer" />
<MenuItem Header="Annulla trasferimento" Click="StopTransfer" />
</ContextMenu>
</ListBox.ContextMenu>
<ListBox.ItemTemplate>
<DataTemplate>
<ProgressBar Height="20" Minimum="0" Maximum="{Binding NChunks}" Name="gasparino_il_carbonaro"
Value="{Binding PbStatus}" Foreground="{Binding Color}" ToolTip="{Binding TooltipInfo}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
When I click, for example, the ContextMenu "GetInfo" voice, I get always the '-1' SelectedIndex (even if I have many bars).
I suppose that the problem is due to the ProgressBar filling (performing steps and so XAML refreshing), when the ProgressBar is filling/refreshing the "system" becomes unable to understand the selected index (which bar I selected with right click?).
My concise question is:
How can I bind the right-clicked bar with the ContextMenuItem relative
method?

Control Template used in Code Behind to add dynamically to a StackPanel

I have the following ControlTemplate in my /Themes/Generic.xaml:
<ControlTemplate x:Key="GroupList" TargetType="{x:Type Expander}">
<Expander IsExpanded="True">
<Expander.Header>
<Label Style="{DynamicResource headline3}" FontWeight="Light" BorderThickness="0 0 0 1">TEst group</Label>
</Expander.Header>
<ListBox x:Name="lstBoxContacts" Height="auto" BorderThickness="0" VerticalAlignment="Top" AllowDrop="True" PreviewDragOver="lstBoxContacts_PreviewDragOver" Drop="lstBoxContacts_Drop" ItemsSource="{Binding Contacts}">
<ListBox.ItemTemplate>
<DataTemplate>
<Label Content="{Binding Fullname}" />
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ContextMenu>
<ContextMenu >
<MenuItem Header="Send Message" Command="local:CustomCommands.cmdMessageWrite"/>
<MenuItem Header="Show Details" Command="local:CustomCommands.cmdContactDetails"/>
<MenuItem Header="Delete" Command="local:CustomCommands.cmdContactDelete"/>
</ContextMenu>
</ListBox.ContextMenu>
</ListBox>
</Expander>
</ControlTemplate>
This should give me an expander with a Listbox in it. To which I can pass a ObservableCollection<User> object, that is than being displayed (Only the Fullname attribute of the user).
In my MainWindow.xaml I have a Stackpanel named "stkpContactsAndGroups" to which I would like to add Expanders programmatically and dynamically.
The contents come from an API call (Which all works).
From what I have seen I should work with Template.FindName. However, when I supply the origin where said template can be found VS bucks me.
Expander grp = (Expander)Template.FindName("GroupList", new Generic());
stkpContactsAndGroups.Children.Add(grp);
It cannot convert Generic() to FrameworkElement. Am I missing the point here?
FrameworkTemplate.FindName method helps you to find an element inside template if this template is applied to templated parent. In you case you'd better use the Application.FindResource method, create new Expander, apply template using the Expander.Template property and after this add this expander to the StackPanel.Children collection

Tree View Context Menu - Pass selected item to command?

I'm wondering how to pass the selected item to a command from a treeview / HierarchicalDataTemplate ?
Here is the code that I have so far, it displays the context menu but I haven't bound the commands to it yet. The command binding is the easy part, but how do I tell which node it came from ?
<HierarchicalDataTemplate
DataType="{x:Type viewModel:UsersViewModel}"
ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal">
<Image Width="16" Height="16" Margin="3,0" Source="Images\Region.png" />
<TextBlock Text="{Binding UserName}">
<TextBlock.ContextMenu>
<ContextMenu>
<MenuItem Header="Edit" />
<MenuItem Header="Delete"/>
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
</StackPanel>
</HierarchicalDataTemplate>
Just {Binding} should be the whole item.
(To pass it to the Command bind the CommandParameter, in Execute and CanExecute it will become the method parameter (which you then need to cast to your item-type))

Is it possible to make a binding in a menu control using list?

I made a style for my menu control and now I want to use that style for all the menuItems, but with different text in the textbox. I was wondering if I can use a List to populate the binding element...I tried but it doesnt work...Have I missed something or I have to use something else?
List<string> itemArray = new List<string>();
itemArray.Add("label1");
itemArray.Add("label2");
itemArray.Add("label3");
Binding binding = new Binding();
binding.Path = new PropertyPath("itemArray");
this.menu1.SetBinding(TextBox.TextProperty, binding);
and the one part of the style is, if it helps...:
<Setter.Value>
<ControlTemplate TargetType="{x:Type MenuItem}">
<Grid>
<Border Name="MainBorder" BorderThickness="2,2,2,0" >
<Grid>
<TextBlock Text="{Binding Path=itemArray}" Margin="30,10,0,0" FontFamily="Arial" FontSize="14" FontWeight="Bold" />
<Image Width="15" Height="15" Source="image.PNG" Margin="-100,0,0,0" />
</Grid>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
You're trying to bind a text element to a List<T>, which will result in the type name. If you want the menu to populate itself from a list of objects, consider binding the menu's ItemsSource property to that list:
<Menu ItemsSource="{Binding ListOfItems}">
<Menu.ItemTemplate>
<DataTemplate>
<MenuItem Header="{Binding Text}" Command="{Binding Command}" />
</DataTemplate>
</Menu.ItemTemplate>
</Menu>
In this example, each list item is an object with a Text property which shows up as the display string, and a Command property, which is an object that implements ICommand. When the user chooses a menu item, that list item's Command.Execute method is invoked; you could use something like RelayCommand or ReactiveCommand to turn that into a method call.
This allows for a flat menu; for a hierarchical menu you'll have to do something a little different.

Categories

Resources