i have an observable collection as ItemSource for my menuitems.
i want a simple button (see commented part) at end of menu item list that can add new item to this collection.
it seem simple But it throw a System.Windows.Markup.XamlParseException in my code telling the collection is already in use.
what is the correct way to achieve this?
<Menu Background="Transparent">
<MenuItem Header="WorkSpace" Background="Transparent" ItemsSource="{Binding NosWorkSpaces}">
<ItemsControl.ItemTemplate>
<DataTemplate >
<StackPanel Orientation="Horizontal">
<Label Content="{Binding Original.Title}"></Label>
<Button Content="Select" Tag="WorkSpace_Load" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=mah:MetroWindow}, Path=DataContext.SelectWorkspaceCommand}" CommandParameter="{Binding }" />
<Button Content="Load" Tag="WorkSpace_Load" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=mah:MetroWindow}, Path=DataContext.LoadBinaryWorkspace}" CommandParameter="{Binding }" />
<Button Content="Save" Tag="WorkSpace_Save" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=mah:MetroWindow}, Path=DataContext.SaveWorkspaceCommand}" CommandParameter="{Binding }" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
<!-- <Button Content="New " /> this wont work -->
</MenuItem>
</Menu>
Well i found a solution mixing some answer, even i find it not so easy for a simple menu.
<Menu Height="24" VerticalAlignment="Top">
<Menu.Resources>
<CollectionViewSource Source="{Binding NosWorkSpaces}" x:Key="YourMenuItems"/>
</Menu.Resources>
<MenuItem Header="WorkSpaces" >
<MenuItem.ItemsSource>
<CompositeCollection>
<CollectionContainer Collection="{Binding Source={StaticResource YourMenuItems}}" />
<Separator></Separator>
<MenuItem Header="Add Worspace" />
</CompositeCollection>
</MenuItem.ItemsSource>
<MenuItem.ItemTemplate>
<DataTemplate >
<StackPanel Orientation="Horizontal">
<Label Content="{Binding Original.Title}"></Label>
<Button Content="Select" Tag="WorkSpace_Load" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=mah:MetroWindow}, Path=DataContext.SelectWorkspaceCommand}" CommandParameter="{Binding }" />
<Button Content="Load" Tag="WorkSpace_Load" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=mah:MetroWindow}, Path=DataContext.LoadBinaryWorkspace}" CommandParameter="{Binding }" />
<Button Content="Save" Tag="WorkSpace_Save" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=mah:MetroWindow}, Path=DataContext.SaveWorkspaceCommand}" CommandParameter="{Binding }" />
</StackPanel>
</DataTemplate>
</MenuItem.ItemTemplate>
</MenuItem>
</Menu>
giving a mixed menu with itemSource and datatemplate.
Related
Hi I want to place a WPF Popup below the last character typed in a textbox just like we know it from intellisense in VS.
My actual Code looks like this is there a way to place it according the coordinates of the textboxex text?:
<local:TextBoxAutoSuggestion Text="{Binding InputText, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
x:Name="SearchPathTextBox"
local:FocusExtension.IsFocused="{Binding IsFocusOnInputTextBox, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
HorizontalAlignment="Stretch"
Foreground="{DynamicResource Menu.Static.Foreground}"
Background="Transparent"
VerticalAlignment="Stretch"
VerticalContentAlignment="Center"
Height="45"
Padding="5,0,30,0"
Margin="0,0,0,0"
CaretBrush="White"
BindableSelectionStart="{Binding SelectionLengthInputText,Mode=TwoWay}">
<i:Interaction.Triggers >
<i:EventTrigger EventName="GotFocus">
<i:InvokeCommandAction Command="{Binding TextBoxFocusedChangedCommand}" />
</i:EventTrigger>
<i:EventTrigger EventName="LostFocus">
<i:InvokeCommandAction Command="{Binding TextBoxFocusedChangedCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<local:TextBoxAutoSuggestion.InputBindings>
<KeyBinding Command="{Binding EnterKeyPressedCommand}"
Key="Return" />
<KeyBinding Command="{Binding TabKeyPressedCommandinTextBox}"
Key="Tab" />
<KeyBinding Command="{Binding BackSpacePressedCommand}"
Key="Backspace" />
<KeyBinding Command="{Binding SelectNextItemCommand}"
Key="Down" />
<KeyBinding Command="{Binding SelectPreviousItemCommand}"
Key="Up" />
</local:TextBoxAutoSuggestion.InputBindings>
</local:TextBoxAutoSuggestion>
<Popup
Width="300"
Margin="0,0,0,0"
StaysOpen="False"
Visibility="{Binding IsPopUpOpened, Converter={StaticResource BooleanToVisibilityConverter}}"
IsOpen="{Binding IsPopUpOpened, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
PlacementTarget="{Binding ElementName=SearchPathTextBox}"
Placement="Bottom"
IsEnabled="{Binding IsPopUpEnabled, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
>
<ListBox Margin="0,0,0,0"
Visibility="{Binding IsPopUpOpened, Converter={StaticResource BooleanToVisibilityConverter}}"
ItemsSource="{Binding FilteredPossibleSegments}"
Foreground="{DynamicResource Menu.Static.Foreground}"
Background="Transparent"
SelectedItem="{Binding SelectedSegment,Mode=TwoWay}"
ScrollViewer.VerticalScrollBarVisibility="Disabled"
Height="200"
>
</ListBox>
</Popup>
The placement which I use right now is "Bottom".
I used horizontal offset property of the pop up and set it according the text length:
<Popup
Width="300"
Margin="0,0,0,0"
StaysOpen="False"
Visibility="{Binding IsPopUpOpened, Converter={StaticResource BooleanToVisibilityConverter}}"
IsOpen="{Binding IsPopUpOpened, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
PlacementTarget="{Binding ElementName=SearchPathTextBox}"
Placement="RelativePoint"
IsEnabled="{Binding IsPopUpEnabled, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
HorizontalOffset="{Binding HorizontalPopUpPosition, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
VerticalOffset="50"
>
I experimented with the horizontal and found the best with this you need to find the best option for your position in your window.
HorizontalPopUpPosition =330+ InputText.Length*5;
My problem is that I have a lot of code doing the same thing. All the buttons in my stackpanel trigger a command and with that command I want to send a parameter to a method in my view model.
I have the following XAML code, as you can see it is ridiculously repetitive:
<StackPanel x:Name="Row" Grid.Row="4">
<Button Content="z"
Command="{Binding ButtonClickCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=Content}" />
<Button Content="x"
Command="{Binding ButtonClickCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=Content}" />
<Button Content="c"
Command="{Binding ButtonClickCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=Content}" />
<Button Content="v"
Command="{Binding ButtonClickCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=Content}" />
<Button Content="b"
Command="{Binding ButtonClickCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=Content}" />
<Button Content="n"
Command="{Binding ButtonClickCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=Content}" />
<Button Content="m"
Command="{Binding ButtonClickCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=Content}" />
</StackPanel>
Is there a way I can reduce the amount of repetitive code, either in the XAML or View Model?
Thanks in advance
Simple solution that works, assuming you have buttons and command binding done at one place or else the first answer (seen the answer after my post) gives you easiest way by putting under an ItemsControl and do the stuff.
<StackPanel x:Name="Row" Grid.Row="4">
<StackPanel.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Command" Value="{Binding ButtonClickCommand}" />
<Setter Property="CommandParameter" Value="{Binding RelativeSource={RelativeSource Self}, Path=Content}"
/>
</Style>
</StackPanel.Resources>
<Button Content="z" />
<Button Content="x" />
<Button Content="c" />
<Button Content="v" />
<Button Content="b" />
<Button Content="n" />
<Button Content="m" />
</StackPanel>
Hope this helps you :)
Create a list of items in your ViewModel, for example
public IEnumerable<string> Values { get; set; } = new List<string> { "x", "c", "v", "..." };
And assign it to the ItemsControl
<ItemsControl x:Name="Row" Grid.Row="4" ItemsSource="{Binding Values}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding }"
Command="{Binding Path=DataContext.ButtonClickCommand, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
CommandParameter="{Binding }"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
ItemsControl.ItemsPanel says what container to use for these items and ItemTemplate defines what each element of StackPanel should look like.
Binding inside DataTemplate refers to string that's why in order to get to the ButtonClickCommand we have to go back to ItemsControl's context.
I'm having trouble getting the selection in a contextMenu to bind to my command EditCommand. The buttons in my tree-view bind to it fine, but in the menu it fails. I have read this is most likely due to the contextMenu being in a different UI tree, but solutions using findAncestor and tags have not worked for me. Is there anyway to do bind and still be able to pass the treeViewItem to the method?
My XAML:
<TreeView Background="Transparent"
Margin="10"
Grid.Column="0" Grid.Row="1"
ItemsSource="{Binding Path=TreeViewItems}">
<TreeView.ItemTemplate >
<HierarchicalDataTemplate DataType="{x:Type model:TreeViewSelection}" ItemsSource="{Binding Configs}" >
<DockPanel HorizontalAlignment="Stretch" Background="Transparent"><!--Transparency allows context click on whole row-->
<DockPanel.ContextMenu>
<ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
<MenuItem
Header="Edit"
Command="{Binding ElementName=userControl, Path=DataContext.EditCommand}"<!--Doesn't work-->
CommandParameter="{Binding}">
<MenuItem.Icon>
<Image Source="../Images/edit.png" />
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</DockPanel.ContextMenu>
<TextBlock DockPanel.Dock="Left" Text="{Binding Title}" />
<StackPanel DockPanel.Dock="Right"
Orientation="Horizontal"
HorizontalAlignment="Right">
<Button Height="23" Width="23"
Command="{Binding ElementName=userControl, Path=DataContext.EditCommand}"<!--Works-->
CommandParameter="{Binding}"
Style="{DynamicResource ImageNoTextButton}"
inf:AttachedProperties.Image="../Images/edit.png"
inf:AttachedProperties.ImageMouseOver="../Images/editMouseOver.png" />
</StackPanel>
</DockPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
Try this:
<DockPanel HorizontalAlignment="Stretch" Background="Transparent"
Tag="{Binding RelativeSource={RelativeSource AncestorType=UserControl}}">
<DockPanel.ContextMenu>
<ContextMenu>
<MenuItem
Header="Edit"
Command="{Binding Path=PlacementTarget.Tag.DataContext.EditCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
CommandParameter="{Binding}">
<MenuItem.Icon>
<Image Source="../Images/edit.png" />
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</DockPanel.ContextMenu>
...
</DockPanel>
My Application consists of a MainWindow with a ContentControl and I change the ViewModel depending on the selected menu.
One of the UserControls I display as content contains the following WrapPanel:
<UserControl ...>
<Grid>
<WrapPanel>
<ItemsControl ItemsSource="{Binding Connections}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Command="{Binding DataContext.ConnectionSelectCommand, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
CommandParameter="{Binding}"
FocusManager.FocusedElement="{Binding ElementName=InstanceName}"
Style="{DynamicResource DashboardButton}">
<TextBlock TextWrapping="Wrap" HorizontalAlignment="Center" Text="{Binding Name}" />
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="Delete"
Command="{Binding ConnectionRemoveCommand}"
CommandParameter="{Binding}" />
</ContextMenu>
</Button.ContextMenu>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</WrapPanel>
</Grid>
</UserControl>
The Command on the ContextMenu doesn't work because it tries to call ConnectionRemoveCommand on the Connection object instead of the ConnectionViewModel which is the DataContext of the UserControl.
How do I bind the Command to the ConnectionViewModel with the CommandParameter being the Connection object?
If you bind the Tag property of the Button to the DataContext of the ItemsControl, you could then bind to it using the PlacementTarget of the ContextMenu:
<Button Command="{Binding DataContext.ConnectionSelectCommand, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
CommandParameter="{Binding}"
FocusManager.FocusedElement="{Binding ElementName=InstanceName}"
Style="{DynamicResource DashboardButton}"
Tag="{Binding DataContext, RelativeSource={RelativeSource AncestorType=ItemsControl}}">
<TextBlock TextWrapping="Wrap" HorizontalAlignment="Center" Text="{Binding Name}" />
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="Delete"
Command="{Binding PlacementTarget.Tag.ConnectionRemoveCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
CommandParameter="{Binding}" />
</ContextMenu>
</Button.ContextMenu>
</Button>
I'm learning WPF/MVVM and got stuck on following...
The code below is working
<ListBox x:Name="listbox" DockPanel.Dock="Top" ItemsSource="{Binding Items}" DisplayMemberPath="Name" >
<ListBox.ContextMenu>
<ContextMenu DataContext="{Binding Path=PlacementTarget, RelativeSource={RelativeSource Self}}">
<MenuItem Header="Show Selected"
Command="{Binding Path=DataContext.ShowSelectedCommand}"
CommandParameter="{Binding Path=SelectedItems}"/>
</ContextMenu>
</ListBox.ContextMenu>
</ListBox>
but when I've replaced listbox on treeview, such as
<TreeView x:Name="tview" DockPanel.Dock="Top" DisplayMemberPath="Name" Tag="{Binding DataContext, RelativeSource={RelativeSource Self}}" ItemsSource="{Binding Items}" >
<TreeView.ContextMenu>
<ContextMenu DataContext="{Binding PlacementTarget.Tag, RelativeSource={RelativeSource Self}}" StaysOpen="true">
<MenuItem Header="Show Selected" Command="{Binding DataContext.ShowSelectedCommand}"
CommandParameter="{Binding Path=SelectedItems}">
</MenuItem>
</ContextMenu>
</TreeView.ContextMenu>
</TreeView>
The ShowSelectedCommand doesn't invoked. Could you please explain what's wrong there and how can I make it work. Thanks a lot.