I have a diagram designer, in which every figure should have editable sign on it. This is how the programm looks like.
As you can see, the textbox with default text appears, but I cant figure out how to save the text input to corresponding "Text" property of DesignerItem class object. DesignerItem.xaml as follows:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="clr-namespace:WPFCanvas"
xmlns:c="clr-namespace:WPFCanvas.Controls">
<ContextMenu x:Key="DesignerItemContextMenu">
...
</ContextMenu>
<!-- Connector Style -->
<Style TargetType="{x:Type s:Connector}">
...
</Style>
<!-- ConnectorDecoratorTemplate Default Template -->
<ControlTemplate x:Key="ConnectorDecoratorTemplate" TargetType="{x:Type Control}">
...
</ControlTemplate>
<!-- ResizeDecorator Default Template -->
<ControlTemplate x:Key="ResizeDecoratorTemplate" TargetType="{x:Type Control}">
...
</ControlTemplate>
<!-- DragThumb Default Template -->
<Style TargetType="{x:Type c:DragThumb}">
...
</Style>
<!-- TextBoxDecorator Default Template -->
<ControlTemplate x:Key="TextBoxDecoratorTemplate" TargetType="{x:Type Control}">
<ContentControl Width="Auto" Height="Auto" VerticalAlignment="Center" HorizontalAlignment="Center">
<TextBox FontSize="11" Margin="1,1,0,0" TextWrapping="Wrap" AcceptsReturn="True"
Background="Transparent" Text="Lorem ipsum dolor sit amet, consectetuer adipiscing elit."/>
</ContentControl>
</ControlTemplate>
<!-- DesignerItem Style -->
<Style TargetType="{x:Type s:DesignerItem}">
<Setter Property="MinWidth" Value="25"/>
<Setter Property="MinHeight" Value="25"/>
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type s:DesignerItem}">
<Grid DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}"
ContextMenu="{StaticResource DesignerItemContextMenu}">
<!-- DragThumb -->
<c:DragThumb x:Name="DragThumb" Cursor="SizeAll"/>
<!-- ResizeDecorator -->
<Control x:Name="ResizeDecorator"
Visibility="Collapsed"
Template="{StaticResource ResizeDecoratorTemplate}"/>
<!-- ContentPresenter -->
<ContentPresenter x:Name="ContentPresenter"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Content="{TemplateBinding ContentControl.Content}"
Margin="{TemplateBinding ContentControl.Padding}"/>
<!-- ConnectorDecorator -->
<Control x:Name="ConnectorDecorator"
Visibility="Hidden"
Template="{StaticResource ConnectorDecoratorTemplate}"/>
<!-- TextBoxDecorator -->
<Control x:Name="TextBoxDecorator"
Template="{StaticResource TextBoxDecoratorTemplate}"/>
</Grid>
<ControlTemplate.Triggers>
<DataTrigger Value="True" Binding="{Binding RelativeSource={RelativeSource Self},Path=Text}">
<Setter TargetName="TextBoxDecorator" Property="Visibility" Value="Visible"/>
</DataTrigger>
<Trigger Property="Text" Value="true">
<Setter TargetName="TextBoxDecorator" Property="Visibility" Value="Visible"/>
</Trigger>
<DataTrigger Value="True" Binding="{Binding RelativeSource={RelativeSource Self},Path=IsSelected}">
<Setter TargetName="ResizeDecorator" Property="Visibility" Value="Visible"/>
</DataTrigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter TargetName="ConnectorDecorator" Property="Visibility" Value="Visible"/>
</Trigger>
<DataTrigger Value="True" Binding="{Binding RelativeSource={RelativeSource Self},Path=IsDragConnectionOver}">
<Setter TargetName="ConnectorDecorator" Property="Visibility" Value="Visible"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And the DesignerItem.cs (without unnecessary code as well):
//These attributes identify the types of the named parts that are used for templating
[TemplatePart(Name = "DragThumb", Type = typeof(DragThumb))]
[TemplatePart(Name = "ResizeDecorator", Type = typeof(Control))]
[TemplatePart(Name = "ConnectorDecorator", Type = typeof(Control))]
[TemplatePart(Name = "ContentPresenter", Type = typeof(ContentPresenter))]
public class DesignerItem : ContentControl, ISelectable, IGroupable
{
public bool IsSelected
{
get { return (bool)GetValue(IsSelectedProperty); }
set { SetValue(IsSelectedProperty, value); }
}
public static readonly DependencyProperty IsSelectedProperty =
DependencyProperty.Register("IsSelected", typeof(bool),
typeof(DesignerItem),
new FrameworkPropertyMetadata(false));
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string),
typeof(DesignerItem));
}
In order to save the text from the visual TextBox to the DesignerItem.Text property, a Binding is needed. The binding target is the TextBox inside the TextBoxDecoratorTemplate and the binding source is the DesignerItem.Text property.
Since you assign the DesignerItem instance as your DataContext (<Grid DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}" .../>), the binding can directly target the Text property of the DataContext contents.
<!-- TextBoxDecorator Default Template -->
<ControlTemplate x:Key="TextBoxDecoratorTemplate" TargetType="{x:Type Control}">
<ContentControl Width="Auto" Height="Auto" VerticalAlignment="Center" HorizontalAlignment="Center">
<TextBox FontSize="11" Margin="1,1,0,0" TextWrapping="Wrap" AcceptsReturn="True"
Background="Transparent" Text="{Binding Text}"/>
</ContentControl>
</ControlTemplate>
A little side note:
Your ControlTemplate.Triggers look like they need some changes. You are triggering a string property against Value="True", which will only trigger when the text contains the string "True". I feel like the idea was more like triggering on something else.
Related
This is my combo-box.
<ComboBox Height="45" HorizontalAlignment="Left" Margin="184,66,0,0" Name="ComboBox1" VerticalAlignment="Top" Width="216">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Label Content="{Binding FullName}" Width="150" />
<Label Content="{Binding Title}" Width="100"/>
<Label Content="{Binding BranchName}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
How can I change it so that only the FullName appears in the textbox portion of the combobox while all three columns still appear in the drop-down portion?
Unfortunately, the SelectionBoxItemTemplate is a readonly property, so we have to do a bit more work. By doing the ItemTemplate to be how you want the item to appear when selected, you can edit the ItemContainerStyle to provide a ControlTemplate that includes the other fields you want to display.
<ComboBox Height="45" HorizontalAlignment="Left" Margin="184,66,0,0" Name="ComboBox1" VerticalAlignment="Top" Width="216">
<ComboBox.ItemTemplate>
<DataTemplate>
<Label Content="{Binding FullName}" Width="150" />
</DataTemplate>
</ComboBox.ItemTemplate>
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ComboBoxItem}">
<Border x:Name="Bd"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}">
<StackPanel Orientation="Horizontal">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
<Label Content="{Binding Title}" Width="100"/>
<Label Content="{Binding BranchName}" />
</StackPanel>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsHighlighted" Value="True">
<Setter TargetName="Bd" Property="Background" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}" />
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
For the ComboBoxItem template, I just modified the default one, so it should be fully functional.
If the ComboBox's IsEditable property is set to True, you can set the "TextSearch.TextPath" property of the ComboBox to the property name you want to show. So in your case:
<ComboBox IsEditable="True" TextSearch.TextPath="FullName" .../>
Instead of using the read-only SelectionBoxItemTemplate property I created a new (attached, writable) property and used that one in my style. I also added a trigger to my style to not break all the comboboxes that are not using my new attached property...
Use it like this:
<ComboBox ItemsSource="{Binding ...}" SelectedItem="{Binding ..., Mode=TwoWay}">
<controls:ComboBoxSelectionBoxAltTemplateBehaviour.SelectionBoxAltTemplate>
<DataTemplate DataType="{x:Type ...}">
... Template for the selection box ...
</DataTemplate>
</controls:ComboBoxSelectionBoxAltTemplateBehaviour.SelectionBoxAltTemplate>
<ComboBox.ItemTemplate>
<DataTemplate DataType="{x:Type ...}">
... Template for the popup ...
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
You just have to add this class to your project:
public class ComboBoxSelectionBoxAltTemplateBehaviour
{
public static readonly DependencyProperty SelectionBoxAltTemplateProperty = DependencyProperty.RegisterAttached(
"SelectionBoxAltTemplate", typeof (DataTemplate), typeof (ComboBoxSelectionBoxAltTemplateBehaviour), new PropertyMetadata(default(DataTemplate)));
public static void SetSelectionBoxAltTemplate(DependencyObject element, DataTemplate value)
{
element.SetValue(SelectionBoxAltTemplateProperty, value);
}
public static DataTemplate GetSelectionBoxAltTemplate(DependencyObject element)
{
return (DataTemplate) element.GetValue(SelectionBoxAltTemplateProperty);
}
}
and change your ComboBox style to use the SelectionBoxAltTemplate attached property if set (or because I could not set a trigger to "not null", I set it back to the default SelectionBoxItemTemplate if the attached one is null):
The ContentPresenter inside the ControlTemplate of the ComboBox Style:
<ContentPresenter Content="{TemplateBinding SelectionBoxItem}" ContentTemplate="{TemplateBinding controls:ComboBoxSelectionBoxAltTemplateBehaviour.SelectionBoxAltTemplate}" />
And the Trigger to provide backwards compatibility to ComboBoxed without the attached Property:
<ControlTemplate.Triggers>
<Trigger Property="controls:ComboBoxSelectionBoxAltTemplateBehaviour.SelectionBoxAltTemplate" Value="{x:Null}">
<Setter Property="ContentTemplate" Value="{Binding SelectionBoxItemTemplate, RelativeSource={RelativeSource TemplatedParent}}" TargetName="ContentSite" />
</Trigger>
...
</ControlTemplate.Triggers>
Full Style:
<Style x:Key="{x:Type ComboBox}" TargetType="{x:Type ComboBox}">
<Setter Property="SnapsToDevicePixels" Value="true" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="FontSize" Value="12" />
<Setter Property="Background" Value="{StaticResource ComboBoxBackground}"/>
<Setter Property="BorderBrush" Value="{StaticResource ComboBoxBorder}"/>
<Setter Property="Margin" Value="6"/>
<Setter Property="Padding" Value="3,3,5,3"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ComboBox}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Border Name="Border" Grid.ColumnSpan="2" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"/>
<ToggleButton Name="ToggleButton2" Focusable="False" IsChecked="{Binding Path=IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" ClickMode="Press" Grid.ColumnSpan="2" Background="Transparent"/>
<!-- Allows clicking anywhere on the combobox, not only the visible button on the right -->
<ToggleButton Focusable="false" Grid.Column="1" x:Name="ToggleButton" IsChecked="{Binding Path=IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" ClickMode="Press" Style="{StaticResource ComboBoxToggleButton}"/>
<ContentPresenter HorizontalAlignment="Left" Margin="{TemplateBinding Control.Padding}" x:Name="ContentSite" VerticalAlignment="Center" Content="{TemplateBinding SelectionBoxItem}" ContentTemplate="{TemplateBinding controls:ComboBoxSelectionBoxAltTemplateBehaviour.SelectionBoxAltTemplate}" ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}" IsHitTestVisible="False" />
<TextBox Visibility="Hidden" HorizontalAlignment="Left" Margin="{TemplateBinding Control.Padding}" x:Name="PART_EditableTextBox" Style="{x:Null}" VerticalAlignment="Center" Focusable="True" Background="Transparent" />
<Popup IsOpen="{TemplateBinding IsDropDownOpen}" Placement="Bottom" x:Name="Popup" Focusable="False" AllowsTransparency="True" PopupAnimation="Slide">
<Grid MaxHeight="{TemplateBinding MaxDropDownHeight}" MinWidth="{TemplateBinding ActualWidth}" x:Name="DropDown" SnapsToDevicePixels="True">
<Border x:Name="DropDownBorder" Background="{StaticResource ComboBoxBackground}" BorderBrush="{StaticResource ComboBoxBorder}" BorderThickness="1" Padding="0,4">
<ScrollViewer SnapsToDevicePixels="True" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" CanContentScroll="True" Style="{x:Null}" >
<StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Contained"/>
</ScrollViewer>
</Border>
</Grid>
</Popup>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="controls:ComboBoxSelectionBoxAltTemplateBehaviour.SelectionBoxAltTemplate" Value="{x:Null}">
<Setter Property="ContentTemplate" Value="{Binding SelectionBoxItemTemplate, RelativeSource={RelativeSource TemplatedParent}}" TargetName="ContentSite" />
</Trigger>
<Trigger Property="HasItems" Value="false">
<Setter Property="MinHeight" Value="95" TargetName="DropDownBorder" />
</Trigger>
<Trigger Property="IsGrouping" Value="true">
<Setter Property="ScrollViewer.CanContentScroll" Value="false" />
</Trigger>
<Trigger Property="IsEditable" Value="true">
<Setter Property="IsTabStop" Value="false" />
<Setter Property="Visibility" Value="Visible" TargetName="PART_EditableTextBox" />
<Setter Property="Visibility" Value="Hidden" TargetName="ContentSite" />
</Trigger>
<Trigger Property="IsMouseOver" Value="true" SourceName="ToggleButton2">
<Setter Property="Background" Value="{StaticResource ComboBoxMouseOver}" />
</Trigger>
<Trigger Property="HasItems" Value="False">
<Setter Property="IsEnabled" Value="False"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
However this might not work with ItemTemplateSelctors, only with one single template - but you could easily add an attached property "SelectionBoxAltTemplateSelector" which provides the selector and passes that one to the style.
There is a pretty good answer to your question here if you don't want to change the ComboBoxes style: https://stackoverflow.com/a/2277488/1070906
It uses a Trigger in the DataTemplate which looks if there is a ComboBoxItem somewhere above in the visual tree, which is not the case in the selection box.
You could override the ComboBox and change the SelectionBoxItemTemplate directly.
public class SelectionComboBox : ComboBox
{
#region Properties
#region Dependency Properties
public DataTemplate AltSelectionBoxItemTemplate
{
get { return (DataTemplate)GetValue(AltSelectionBoxItemTemplateProperty); }
set { SetValue(AltSelectionBoxItemTemplateProperty, value); }
}
public static readonly DependencyProperty AltSelectionBoxItemTemplateProperty =
DependencyProperty.Register("AltSelectionBoxItemTemplate", typeof(DataTemplate), typeof(SelectionComboBox), new UIPropertyMetadata(null, (s, e) =>
{
// For new changes...
if ((s is SelectionComboBox) && ((SelectionComboBox)s).Presenter != null && (e.NewValue is DataTemplate))
((SelectionComboBox)s).Presenter.ContentTemplate = (DataTemplate)e.NewValue;
// Set the new value
((SelectionComboBox)s).AltSelectionBoxItemTemplate = (DataTemplate)e.NewValue;
}));
#endregion
#region Internals
#region Elements
ContentPresenter Presenter { get; set; }
#endregion
#endregion
#endregion
#region Constructors
#endregion
#region Methods
#region Overrides
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
Presenter = this.GetTemplateChild("contentPresenter") as ContentPresenter;
// Directly Set the selected item template
if (AltSelectionBoxItemTemplate != null) Presenter.ContentTemplate = AltSelectionBoxItemTemplate;
}
#endregion
#endregion
}
Once you define the control, you could style it.
<controls:SelectionComboBox ItemsSource="{Binding ...}" SelectedItem="{Binding ..., Mode=TwoWay}">
<controls:SelectionComboBox.AltSelectionBoxItemTemplate>
<DataTemplate>
<!-- My Template Goes Here... -->
</DataTemplate>
</controls:SelectionComboBox.AltSelectionBoxItemTemplate>
</controls:SelectionComboBox>
I've created a small WPF application and have a number of TextBoxes on the page. All of these TextBoxes have rounded corners.
I have used the MVVM pattern and implemented the IDataErrorInfo interface to display errors to the user. When one of the TextBoxes is empty the edges, ToolTips should be displayed Red which I have successfully done.
I now want the red edge to also have rounded corners like the TextBoxes. As shown in the image the red border is shown as the TextBox is empty, The red border needs to have a corner radius.
<!-- Text box xaml code that is used to display the error and binding -->
<TextBox Style="{StaticResource TextBoxBase}"
Name="FirstName"
Text="{Binding FirstName, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay, Source={StaticResource CustomerObject}, ValidatesOnDataErrors=True}"/>
<!-- Code that changes the tool tip if it's null -->
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors).CurrentItem.ErrorContent}" />
</Trigger>
</Style.Triggers>
You could create a specific Tooltip Style for errors:
Here an example of a custom Tooltip style with CornerRadius = "4":
<Style x:Key="ErrorRoundedTooltip" TargetType="ToolTip">
<Setter Property="OverridesDefaultStyle" Value="true" />
<Setter Property="HasDropShadow" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToolTip">
<Border Name="Border" CornerRadius="4"
BorderThickness="1"
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}">
<ContentPresenter Margin="4" HorizontalAlignment="Left" VerticalAlignment="Top" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Content">
<Setter.Value>
<ItemsControl ItemsSource="{Binding Path=(Validation.Errors)}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ErrorContent.ValidationMessage}" VerticalAlignment="Center"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Setter.Value>
</Setter>
</Style>
<ToolTip x:Key="ErrorRepository" Style="{StaticResource ErrorRoundedTooltip}" />
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="ToolTip" Value="{StaticResource ErrorRepository}" />
</Trigger>
</Style.Triggers>
</Style>
In my grid I have a column which needs to provide a basic set of dropdown options, but which the user can manually edit. I've got this part working just fine:
<DataGridTemplateColumn Header="Comment" Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding DefaultComments}" Text="{Binding Comment, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Center" VerticalContentAlignment="Center" IsEditable="True" Name="CommentComboBox" Loaded="CommentComboBox_Loaded" Height="50"></ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
The additional feature I'd like to provide is to allow multi-line text entry where the user can force a line feed using Ctrl-Enter.
Is it possible to extend the existing column to do this by perhaps manipulating the TextBox portion of the ComboBox? Or will it require a completely different template column?
Try This below will allow you to add multiline input in textbox inside a combobox.
Add the style to the Resources
This will work for Enter rather than using Ctrl-Enter.
<Window.DataContext>
<local:MyComboVM/>
</Window.DataContext>
<Window.Resources>
<SolidColorBrush x:Key="SelectedBackgroundBrush" Color="#DDD" />
<SolidColorBrush x:Key="DisabledForegroundBrush" Color="#888" />
<Style x:Key="{x:Type ComboBoxItem}" TargetType="ComboBoxItem">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBoxItem">
<Border Name="Border" Padding="2" SnapsToDevicePixels="true">
<StackPanel>
<TextBlock Text="{Binding Path=Text, BindsDirectlyToSource=True}"></TextBlock>
<TextBox TextWrapping="Wrap" AcceptsReturn="True"></TextBox>
</StackPanel>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsHighlighted" Value="true">
<Setter TargetName="Border" Property="Background" Value="{StaticResource SelectedBackgroundBrush}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{StaticResource DisabledForegroundBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<ComboBox ItemsSource="{Binding MyCombo}"/>
</Grid>
VM
public class MyComboVM
{
public ObservableCollection<Texts> MyCombo { get; set; }
public MyComboVM()
{
MyCombo = new ObservableCollection<Texts>();
MyCombo.Add(new Texts() { Text = "This is one" });
MyCombo.Add(new Texts() { Text = "This is Two" });
}
}
public class Texts
{
public string Text { get; set; }
}
I've got a ListBox that uses a Style to define how the ListBoxItems look. Here is the style:
<Style x:Key="OutputListBoxStyle" TargetType="ListBox">
<Setter Property="TextElement.FontFamily" Value="Consolas" />
<Setter Property="TextElement.FontSize" Value="12" />
<Setter Property="VirtualizingStackPanel.IsVirtualizing" Value="True" />
<Setter Property="VirtualizingStackPanel.VirtualizationMode" Value="Recycling" />
<Setter Property="hyp:ListBoxSelector.Enabled" Value="True" />
<Setter Property="ItemContainerStyle" Value="{StaticResource OutputListBoxItemStyle}" />
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Command="Copy" />
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="OutputListBoxItemStyle" TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<TextBlock Text="{Binding Text}" TextWrapping="Wrap"
Background="{TemplateBinding Background}"
Foreground="{Binding ForegroundColor}"
FontWeight="{TemplateBinding FontWeight}"/>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter Property="Background" Value="Blue" />
<Setter Property="Foreground" Value="{StaticResource {x:Static SystemColors.HighlightTextBrushKey}}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
As you can see, the Foreground property of the TextBlock within the ListBoxItem is binding to my object's ForegroundColor property. I've also tried binding with a Setter in the ListBoxItem itself instead of the TextBlock, and having the TextBlock use a TemplateBinding like the Background property, but this also doesn't work. Here is the object I am binding to:
public class PrintInfo : ViewModelBase
{
protected Brush foreColor;
public String Text { get; set; }
public Brush ForegroundColor { get { return foreColor; } set { foreColor = value; RaisePropertyChanged("ForegroundColor"); } }
public PrintInfo()
{
ForegroundColor = Brushes.White;
}
public PrintInfo(String text) : base()
{
Text = text;
}
}
The text is being printed, but with no foreground color, so it isn't visible. I want the foreground color to be white. I've seen other SO questions that bind in a very similar way to what I'm trying to do, so I'm confused why this isn't working. Here's the ListBox itself:
<ListBox Name="OutputBox" Grid.Column="0" ItemsSource="{Binding Output,RelativeSource={RelativeSource FindAncestor,AncestorType=Window}}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Visible"
Background="{DynamicResource ControlBackgroundBrush}"
Style="{StaticResource OutputListBoxStyle}">
EDIT 1: Added the ListBox's style to this example per Rohit's request.
EDIT 2: Just so people don't have to search through my comments, the problem wasn't in any of my XAML, it was in my custom object, PrintInfo. I was calling : base() instead of : this(), and expecting the default constructor to be called, which of course it wasn't, the superclass constructor was being called. The Foreground color was never set because of this, and thus my invisible text results.
In your code, ListBoxItem style is not applied anywhere on ListBox instance. Moreover, I don't see any need to override the Template of ListBoxItem, you can set the ItemTemplate like this:
<ListBox Name="OutputBox" Grid.Column="0"
ItemsSource="{Binding Output, RelativeSource={RelativeSource FindAncestor,
AncestorType=Window}}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Visible"
Background="{DynamicResource ControlBackgroundBrush}"
Style="{StaticResource OutputListBoxStyle}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Text}" TextWrapping="Wrap">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="{Binding ForegroundColor}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource FindAncestor, AncestorType=ListBoxItem}}"
Value="True">
<Setter Property="Background" Value="Blue" />
<Setter Property="Foreground" Value="{StaticResource {x:Static SystemColors.HighlightTextBrushKey}}" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I have the following Button and Style in WPF and I need to generalize the Binding in the DataTrigger section because I have near 10 similar buttons in the same Window and each button should be binded to a different property (SelectedPositions, SelectedAgencies, ....). Is it possible to implement?
<Button x:Name="btnPosition"
Grid.Row="0"
Grid.Column="0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Command="{Binding PositionFilterCommand}"
Content="{l:Translate position}"
Style="{StaticResource NewButtonStyle}" />
<Style x:Key="NewButtonStyle" TargetType="{x:Type Button}">
<Setter Property="Foreground" Value="White" />
<Setter Property="Height" Value="22" />
<Setter Property="Width" Value="Auto" />
<Setter Property="FontFamily" Value="OpenSans" />
<Setter Property="FontSize" Value="13" />
<Setter Property="Cursor" Value="Hand" />
<Setter Property="Margin" Value="10,2,10,0" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border CornerRadius="3">
<Grid x:Name="gridButton" Background="#54728e">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image x:Name="img"
Grid.Column="0"
Width="24"
Height="24"
Source="Img/tick-white.png"
Visibility="Visible" />
<Rectangle x:Name="rect"
Grid.Column="1"
Fill="#54728e"
RadiusX="3"
RadiusY="3" />
<ContentPresenter Grid.Column="1"
Margin="5,0,5,0"
HorizontalAlignment="Stretch"
VerticalAlignment="Center" />
</Grid>
</Border>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding SelectedPositions}" Value="{x:Static sys:String.Empty}">
<Setter TargetName="rect" Property="Fill" Value="#8bbcdf" />
<Setter TargetName="img" Property="Visibility" Value="Collapsed" />
<Setter TargetName="gridButton" Property="Background" Value="#8bbcdf" />
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
could you provide me an example of what you explained?
Sure,
1 - Using Tag
In your Style have your DataTrigger as:
<DataTrigger Binding="{Binding Path=Tag,
RelativeSource={RelativeSource Self}}"
Value="{x:Static sys:String.Empty}">
...
</DataTrigger>
as for usage:
<Button x:Name="btnPosition"
Grid.Row="0"
Grid.Column="0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Command="{Binding PositionFilterCommand}"
Content="{l:Translate position}"
Tag="{Binding SelectedPositions}"
Style="{StaticResource NewButtonStyle}" />
2 - Using Attached Property:
"local:" refers to the xaml namespace alias of your application or if you use different namespaces, the namespace where MyCustomPropertyCollection is declared.
code-behind:
public class MyCustomPropertyCollection {
public static readonly DependencyProperty SomeStringProperty =
DependencyProperty.RegisterAttached(
"SomeString",
typeof(string),
typeof(MyCustomPropertyCollection),
new FrameworkPropertyMetadata(string.Empty));
public static void SetSomeString(UIElement element, string value) {
element.SetValue(SomeStringProperty, value);
}
public static string GetSomeString(UIElement element) {
return (string)element.GetValue(SomeStringProperty);
}
}
Style.DataTrigger
<DataTrigger Binding="{Binding Path=(local:MyCustomPropertyCollection.SomeString),
RelativeSource={RelativeSource Self}}"
Value="{x:Static sys:String.Empty}">
...
</DataTrigger>
usage:
<Button x:Name="btnPosition"
Grid.Row="0"
Grid.Column="0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Command="{Binding PositionFilterCommand}"
Content="{l:Translate position}"
local:MyCustomPropertyCollection.SomeString="{Binding SelectedPositions}"
Style="{StaticResource NewButtonStyle}" />
3 - Normal Dependency Property
custom Button class:
public class MyButton : Button {
public static readonly DependencyProperty SomeStringProperty =
DependencyProperty.Register(
"SomeString",
typeof(string),
typeof(MyButton),
new FrameworkPropertyMetadata(string.Empty));
public string SomeString {
get {
return (string)GetValue(SomeStringProperty);
}
set {
SetValue(SomeStringProperty, value);
}
}
}
Style in xaml not only needs DataTrigger updated but Style definition too.
so switch
<Style x:Key="NewButtonStyle" TargetType="{x:Type Button}">
to
<Style x:Key="NewButtonStyle" TargetType="{x:Type local:MyButton}">
Style.DataTrigger
<DataTrigger Binding="{Binding Path=SomeString,
RelativeSource={RelativeSource Self}}"
Value="{x:Static sys:String.Empty}">
...
</DataTrigger>
usage:
<local:MyButton x:Name="btnPosition"
Grid.Row="0"
Grid.Column="0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Command="{Binding PositionFilterCommand}"
Content="{l:Translate position}"
SomeString="{Binding SelectedPositions}"
Style="{StaticResource NewButtonStyle}" />
Tag approach is frowned upon. "Attached Property" is easier to implement but isn't as clear of a indicator of dependencies as a custom class with a normal DP and AP also gets way overused. Take your pick for what you'd prefer.