I have a treeview which contains files, every view model holds an item source which is an ObservableCollection with files items:
public ObservableCollection<CMItemFileNode> SubItemNode
On each item i have context menu options (Delete, Execute..).
If i move from one viewModel to another the ObservableCollection of files updated correctly and presented correctly but, when i perform a context menu command like delete file item, the command execute good but when i move to another view model (which holds SubItemNode ObservableCollection of is own) after the command executed the WPF still thinks i'm in the last view model i was in and not the one i'm really on.
Very important to mention is that when i update to .net 4.5 (which unfortunantly i can't do) everything is ok and the ObservableCollection addresses the correct view model.
Here is the treeView:
<TreeView x:Name="Files" Margin="0,5,5,0" Grid.Row="6" Grid.Column="2" ItemsSource="{Binding SubItemNode}" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch" VerticalAlignment="Stretch" Height="300" Grid.RowSpan="6" Width="300" dd:DragDrop.IsDragSource="True" dd:DragDrop.IsDropTarget="True" dd:DragDrop.DropHandler="{Binding}" dd:DragDrop.UseDefaultDragAdorner="True">
<TreeView.Resources>
<Style TargetType="{x:Type TreeView}">
<Setter Property="local:CMTreeViewFilesBehavior.IsTreeViewFilesBehavior" Value="True"/>
</Style>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected}" />
<Setter Property="local:CMTreeViewFilesItemBehavior.IsTreeViewFilesItemBehavior" Value="True"/>
</Style>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Transparent" />
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}" Color="Black" />
</TreeView.Resources>
<TreeView.ContextMenu>
<ContextMenu>
<MenuItem Header="View File" Command="{Binding ExecuteFileCommand}" />
<Separator />
<MenuItem Header="Delete all" Command="{Binding DeleteAllFilesCommand}" />
<MenuItem Header="Delete selected" Command="{Binding DeleteSelectedFilesCommand}" />
</ContextMenu>
</TreeView.ContextMenu>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding SubItemNode}" >
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Margin="2" Width="32" Height="18" Source="{Binding Path=Icon}" HorizontalAlignment="Left" VerticalAlignment="Center" />
<TextBlock Text="{Binding Path=Name}" Grid.Column="1" Margin="2" VerticalAlignment="Center" Foreground="{Binding Path=Status, Converter={StaticResource ItemFileStatusToColor}}" FontWeight="{Binding Path=IsSelected, Converter={StaticResource BoolToFontWidth}}"/>
</Grid>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
Am I doing somthing wrong? and why in .net 4.5 it works well ?
Apparently in .net 4 after executing a context menu command we lose context inside the tree.
To solve this we need to add this to the context menu:
<ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}" >
If we upgrade to .net 4.5 it's not necessary.
Related
I have a ListBox which is dynamically filled during runtime. It has a DataType of RequirementPreview which is needed to bind certain properties to triggers, sources and parameters. There is, however, 1 command-binding that needs to trigger a command that is set in the datacontext but has nothing to do with the template datatype.
What I want is that the Button fires the 'RevertDelete'-command and sends the Guid of the RequirementPreview to which the button belongs. As of now, nothing happens when I press the button.
Xaml:
<ListBox Grid.Row="1" Grid.ColumnSpan="3" Margin="5" IsEnabled="{Binding IsEnabled}" ItemsSource="{Binding Requirements}" SelectionMode="Extended">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<cmd:EventToCommand Command="{Binding SelectionChanged}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Style.Triggers>
<DataTrigger Binding="{Binding IsDeleted}" Value="True">
<Setter Property="Foreground" Value="red"/>
</DataTrigger>
</Style.Triggers>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate DataType="{x:Type myModels:RequirementPreview}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="45"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Name}" ToolTip="{Binding Name}" HorizontalAlignment="Stretch"/>
<Button Grid.Column="2" HorizontalAlignment="Right" IsEnabled="{Binding IsEnabled}" Visibility="{Binding IsDeleted, Converter={StaticResource BoolToVisibility}}"
Command="{Binding RevertDelete}" CommandParameter="{Binding Guid}" Height="10" Width="10"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Xaml.cs:
[Dependency]
public IMainViewModel ViewModel
{
get { return this.DataContext as IMainViewModel; }
set { this.DataContext = value; }
}
Edit:
Tried messing around with the databinding;
Command="{Binding RelativeSource={x:Static RelativeSource.PreviousData}, Path=RevertDelete}"
Command="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=RevertDelete}"
Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}, Path=RevertDelete}
Neither made a difference.
I guess RevertDelete command is not a part of RequirementPreview type which works as a data context for the template. You'll need to use relative source binding for this command:
<Button Command="{Binding DataContext.RevertDelete, RelativeSource={RelativeSource AncestorType=ListBox}}"
CommandParameter="{Binding Guid}" />
I have searched around for this, found some solutions related to Winforms, and some even just saying it is really difficult in WPF to accomplish, but those posts are quite old.
If I have a standard ListBox, which is declared as:
<ListBox
x:Name="listBox"
HorizontalAlignment="Left"
Height="240"
Margin="401,68,0,0"
VerticalAlignment="Top"
Width="345"
SelectionChanged="listBox_SelectionChanged"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
Grid.ColumnSpan="2"/>`
and programmatically:
System.ComponentModel.BindingList<string> listItems = new System.ComponentModel.BindingList<string>();
listBox.ItemsSource = listItems;
Is there a way for these strings to be wrapped within the ListBox?
Not hard at all:
<ListBox
....
>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock
Text="{Binding}"
TextWrapping="Wrap"
/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
If you got StackPanel please get rid of them. They are bad with wrapping. Use GRID instead like #EdPlunkett suggested.
To use this:
<ItemsControl ItemsSource="{Binding MyErrors, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Template="{StaticResource ErrorListContainerTemplate}"
ItemContainerStyle="{StaticResource ErrorListStyle}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
/>
Here is my style code:
<Style TargetType="{x:Type TextBlock}" x:Key="WrappingStyle">
<Setter Property="TextWrapping" Value="WrapWithOverflow"/>
</Style>
<Style TargetType="ContentPresenter" x:Key="ErrorListStyle">
<Setter Property="TextBlock.Foreground" Value="{DynamicResource TextBoxBorderErrorColor}"/>
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Border Margin="0,5">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Path Grid.Column="0" Fill="{DynamicResource TextBoxBorderErrorColor}" VerticalAlignment="Top" HorizontalAlignment="Left">
<Path.Data>
<EllipseGeometry RadiusX="2.5" RadiusY="2.5"/>
</Path.Data>
</Path>
<ContentPresenter Grid.Column="1" Content="{Binding}" VerticalAlignment="Top">
<ContentPresenter.Resources>
<Style TargetType="{x:Type TextBlock}" BasedOn="{StaticResource WrappingStyle}"/>
</ContentPresenter.Resources>
</ContentPresenter>
</Grid>
</Border>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
To summarize before posting my XAML, this form is very simple for now. There is a ListBox, two buttons and a context menu for the ListBox.
If the right mouse button is clicked in the ListBox, and NO ELEMENT is selected, my context menu, performs the ADD operation, which right now is just simply popping up a message box.
When an element is selected, such as MODIFY, my bindings do not work. So I am assuming, after much reading, that I have an inheritance problem somewhere. I have tried using DataContext, RelativeSource, etc...and still no joy.
Here is my XAML
<Window x:Class="FracasReportSettings.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ignore="http://www.ignore.com"
mc:Ignorable="d ignore"
Height="402"
Width="578"
Title="FRACAS Ticket Value Modification"
DataContext="{Binding Main, Source={StaticResource Locator}}">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Skins/MainSkin.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid x:Name="LayoutRoot">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="91*"/>
<ColumnDefinition Width="194*"/>
</Grid.ColumnDefinitions>
<TextBlock FontSize="36"
FontWeight="Bold"
Foreground="Purple"
Text="{Binding PageTitle}"
VerticalAlignment="Center"
HorizontalAlignment="Center"
TextWrapping="Wrap" Margin="266,27,10,9" Width="112" Grid.Column="1" />
<StackPanel HorizontalAlignment="Left" Height="311" Margin="40,22,0,0" VerticalAlignment="Top" Width="111">
<Button Content="Detection Method" Margin="10,10,10,10"
Command="{Binding MyBinding}"
CommandParameter="DetectionMethod"/>
<Button Content="Button" Margin="10,0,10,0"/>
</StackPanel>
<ListBox
Name="AdminList"
ItemsSource="{Binding Names}"
Height="302" Width="231" Margin="151,22,0,0"
HorizontalAlignment="Left" VerticalAlignment="Top" Grid.ColumnSpan="2"
SelectedItem="{Binding SomeName}"
>
<ListBox.Resources>
<Style TargetType="ListBoxItem">
<Style.Triggers>
<Trigger Property="IsKeyboardFocusWithin" Value="True">
<Setter Property="IsSelected" Value="True"/>
</Trigger>
</Style.Triggers>
</Style>
</ListBox.Resources>
**<-- THIS WORKS FINE -->
<ListBox.ContextMenu>
<ContextMenu>
<MenuItem Header="Add"
Command="{Binding RCContentMenu}">
</MenuItem>
</ContextMenu>
</ListBox.ContextMenu>**
<--MY ERROR IS IN HERE-->
**<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}, Path=DataContext}">
<MenuItem Header="Modify"
Command="{Binding Path=ModifyElementCommand}"
CommandParameter="{Binding Path=SelectedItem}"/>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>**
</ListBox>
</Grid>
The ModifyElementCommand is sitting in a ViewModel named MainViewModel.
The error I receive from the output window is:
System.Windows.Data Error: 4 : Cannot find source for binding with
reference 'RelativeSource FindAncestor,
AncestorType='System.Windows.Controls.ListBox', AncestorLevel='1''.
BindingExpression:Path=DataContext; DataItem=null; target element is
'ContextMenu' (Name=''); target property is 'DataContext' (type
'Object')
Which I know means that the dependency cannot be found.
What should I do to fix this?
Following my comment here is the XAML:
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu DataContext="{Binding RelativeSource={RelativeSource Self},Path=PlacementTarget.Tag.DataContext}">
<MenuItem Header="Modify"
Command="{Binding Path=ModifyElementCommand}"
CommandParameter="{Binding Path=SelectedItem}"/>
</ContextMenu>
</Setter.Value>
</Setter>
<Setter Property="Tag" Value="{Binding ElementName=AdminList}" />
</Style>
</ListBox.ItemContainerStyle>
I have a ListBox...
<ListBox Margin="10" ItemsSource="{Binding Employees}"
ItemTemplate="{DynamicResource EmployeesTemplate}"
HorizontalContentAlignment="Stretch" BorderThickness="0"
ScrollViewer.CanContentScroll="False"/>
...that has a custom DataTemplate:
<Window.Resources>
<DataTemplate x:Key="EmployeesTemplate">
<Border BorderThickness="1" BorderBrush="Black" SnapsToDevicePixels="True"
DockPanel.Dock="Top" Margin="0,0,0,5" Padding="5">
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" Orientation="Horizontal">
<TextBlock Text="{Binding Path=FirstName}" Padding="0,0,5,0"/>
<TextBlock Text="{Binding Path=LastName}"/>
</StackPanel>
<TextBlock Grid.Column="1" Text="{Binding Path=Title}"/>
</Grid>
<StackPanel>
<TextBlock Text="{Binding Path=DateOfBirth, StringFormat={}{0:MM/dd/yyyy}}"/>
<TextBlock Text="{Binding Path=Address}"/>
<TextBlock Text="{Binding Path=PhoneNumber}"/>
<TextBlock Text="{Binding Path=Salary, StringFormat={}{0:C}}"/>
</StackPanel>
</StackPanel>
</Border>
</DataTemplate>
</Window.Resources>
When I select an item from the ListBox, the selection highlighting includes the 5-point Margin that is assigned to the bottom of each item (Border in DataTemplate):
You'll notice the similar situation on the left side, where the highlight overflows just a little bit. I did not notice that until now... hmm. So, I would like to restrict the selection highlight to the border area and nothing outside of it and be able to retain the margin spacing between the items.
How would I accomplish that? I tried manipulating Padding and Margin as much as I could, but I could not figure it out. Maybe I have to create a custom ListBox template?
I'm not sure if there is a simpler way but it works for me. I added a DataTemplate.Triggers section to my DataTemplate...
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type ListBoxItem}}, Path=IsMouseOver}" Value="True">
<Setter Property="Background" Value="LightBlue" TargetName="EmployeesTemplateBorder"/>
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type ListBoxItem}}, Path=IsSelected}" Value="True">
<Setter Property="Background" Value="RoyalBlue" TargetName="EmployeesTemplateBorder"/>
</DataTrigger>
</DataTemplate.Triggers>
...and a ListBox.Resrouces section to the ListBox (to get rid of the default highlight behavior):
<ListBox.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Transparent" />
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}" Color="Black" />
<SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" Color="Transparent" />
</ListBox.Resources>
Obviously, that doesn't do anything to the foreground, but that's not the concern right now, since that one is relatively easy comparing to this highlight routine.
i have problem with styling the combobox's togglebutton.
my combobox xaml code looks like this:
<ComboBox Width="Auto"
HorizontalContentAlignment="Stretch"
FontFamily="HelveticaNeue-Bold"
FontSize="20"
FontWeight="Bold"
Foreground="#FFC0C0C0"
Padding="0,0,0,0"
Style="{DynamicResource navigationComboBox}"
ItemsSource="{Binding Tournaments}"
SelectedValue="{Binding SelectedTournament}">
<ComboBox.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Transparent" />
</ComboBox.Resources>
<ComboBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True"
Orientation="Vertical"
Width="auto"
Height="{Binding Tournaments, Converter={StaticResource CollectionToHeightConverter}}"/>
</ItemsPanelTemplate>
</ComboBox.ItemsPanel>
<ComboBox.ItemTemplate>
<DataTemplate>
<DockPanel x:Name="comboDock">
<DockPanel.Background>
<ImageBrush ImageSource="{Binding Converter={StaticResource ImagePathConverter}, ConverterParameter=comboboxitem-line.png}" />
</DockPanel.Background>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="41" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<DockPanel x:Name="comboArrow"
Grid.Column="0"
Visibility="Collapsed">
<DockPanel.Background>
<ImageBrush ImageSource="{Binding Converter={StaticResource ImagePathConverter}, ConverterParameter=SportsSubMenuActive.png}" />
</DockPanel.Background>
</DockPanel>
<TextBlock x:Name="comboText"
Grid.Column="1"
FontFamily="HelveticaNeue-Bold"
FontSize="20"
FontWeight="Bold"
Foreground="#FFC0C0C0"
Padding="0,0,10,0"
Text="{Binding Path=Name}"
TextAlignment="Left"
TextWrapping="Wrap" />
</Grid>
</DockPanel>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource AncestorType=ComboBoxItem}}" Value="True">
<Setter TargetName="comboArrow" Property="Visibility" Value="Visible" />
<Setter TargetName="comboText" Property="Foreground" Value="#F94B01" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ComboBox.ItemTemplate>
so i'm trying to remove the background set to on togglebutton, but it is not possible. if i remove the background on itemtemplate, then i can style the toggle button background. is there any special order in the process to prevent me from changing the background of the togglebutton content if it is set in itemtemplate?
thanks in advance guys,
Kristo
Setting a property directly takes priority over any Setter(s). Instead of setting the property directly, use a Style for your DockPanel and use a Setter to set the background similar to what you do in your DataTrigger. That should allow you to change the background property elsewhere.