Converter for bound data in style - wpf

Can't see the trees through the forest.
Trying a simple databinding and I want to format the value with a converter. (In this converter example, numeric data that is 0 is not displayed.)
Resource:
<conv:FormattingConverter x:Key="FormattingConverter"/>
<Style x:Key="EGTSTextBoxInt" TargetType="TextBox">
<Setter Property="Background" Value="{StaticResource CC_BACKGROUND}" />
<Setter Property="Foreground" Value="{StaticResource CC_FOREGROUND}" />
<Setter Property="FontFamily" Value="{StaticResource DefaultFont}" />
<Setter Property="FontSize" Value="{StaticResource DefaultFontSize}" />
<Setter Property="FontWeight" Value="Normal" />
<Setter Property="FontStyle" Value="Normal" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="VerticalAlignment" Value="Center"
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="IsReadOnly" Value="True" />
</Style>
Textbox:
<TextBox Name="Bill_Item"
Grid.Column="6"
MinWidth="46"
MinHeight="23"
Style="{StaticResource EGTSTextBoxInt}"
Text="{Binding Path=Item, Mode=TwoWay,
Converter={StaticResource FormattingConverter},
ConverterParameter=\{0:G\}}" />
What I want to do is get the Converter code embedded in the style so that I don't have to spell it out in each Text=line.
Adding code behind the XAML to do anything for this is not an option!
I am looking for a pure XAML method.
I am still trying to grasp how certain things are done in Styles.
Brian

There are several ways to do this.
But before you even start:
It's heavy
It's utterly useless in your case
1) Define a custom markup extension
The idea is to inherit from the Binding markup extension that allows you to write Text ="{Binding SomeProperty}".
You can find an example of someone who made his own binding here
This solution does not allow you to put the feature in a style.
2) Define an attached property
that will modify the Binding of the default property of a control (in your case, Text is the default property of TextBox, as you can do <TextBox>my text</TextBox>) in order to use the converter you want, automatically.
This actually allows you to put set this attached property in a style. It is, however, very bad practice. It's as bad a magic strings.
3)Don't do this, just don't.
Write the converter each time.

The Text property of the TextBox is about data. The Style property is about what that data looks like. It doesn't make sense to attach a Converter to a Style.
Edit: I think I see what you're trying to say now. You want Text="{Binding Path=Item}" in one place and <Setter Property="Text" Value="~somehow get the converter in here and have it applied to the existing text property which is bound to Items~" />
As mydogisbox mentioned, I don't think you can split up these ideas in two places like that.

Converters are for converting one value into another value, so they cannot be used without the value they are converting
You can however use StringFormat in your binding to format values without a Converter
<TextBox Text="{Binding Path=Item, StringFormat=G}" />
You could also create a class that inherits from Binding class and sets the default StringFormat, although I feel this is more trouble than it's worth
<TextBox Text="{local:NumberBinding Path=Item}" />
And if you are ever working with Labels instead of TextBoxes, you can apply a style setter to ContentStringFormat, which will apply formatting to the Label's Content
<Style TargetType="{x:Type Label}">
<Setter Property="ContentStringFormat" Value="G" />
</Style>

Related

Model "namespacing" for wpf StyleSelector?

I am learning how to use avalonDock by debugging its test sample apps. Something I have not been able to explain at this stage is the presence of the "Model" Keyword in some xaml binding.
Mainly in the style selector, if you remove it, the binding does not work anymore:
<avalonDock:DockingManager.LayoutItemContainerStyleSelector>
<local:PanesStyleSelector>
<local:PanesStyleSelector.ToolStyle>
<Style TargetType="{x:Type avalonDock:LayoutAnchorableItem}">
<Setter Property="Title" Value="{Binding Model.Title}"/>
<Setter Property="IconSource" Value="{Binding Model.IconSource}"/>
<Setter Property="Visibility" Value="{Binding Model.IsVisible, Mode=TwoWay, Converter={StaticResource BoolToVisibilityConverter}, ConverterParameter={x:Static Visibility.Hidden}}"/>
<Setter Property="ContentId" Value="{Binding Model.ContentId}"/>
<Setter Property="IsSelected" Value="{Binding Model.IsSelected, Mode=TwoWay}"/>
<Setter Property="IsActive" Value="{Binding Model.IsActive, Mode=TwoWay}"/>
</Style>
</local:PanesStyleSelector.ToolStyle>
<local:PanesStyleSelector.FileStyle>
<Style TargetType="{x:Type avalonDock:LayoutItem}">
<Setter Property="Title" Value="{Binding Model.Title}"/>
<Setter Property="ToolTip" Value="{Binding Model.FilePath}"/>
<Setter Property="CloseCommand" Value="{Binding Model.CloseCommand}"/>
<Setter Property="IconSource" Value="{Binding Model.IconSource}"/>
<Setter Property="ContentId" Value="{Binding Model.ContentId}"/>
</Style>
</local:PanesStyleSelector.FileStyle>
</local:PanesStyleSelector>
</avalonDock:DockingManager.LayoutItemContainerStyleSelector>
MainWindow.xaml
PaneStyleSelector
Why ? I have not been able to figure out any "Model" Field or property. And it seems to work without it for other examples:
Microsoft Example
The nice thing about XAML is that there always seems to be a way to do things a number of ways. This, of course, makes it difficult to get ahead of the learning curve sometimes too. In short, whenever you see {Binding _______} think DataContext.
Also, know that object navigation in XAML is similar to IntelliSense when typing in a normal .cs file. Meaning, if you have:
public class A { public B b = new B(); }
public class B { public string C; }
then you can get to A's B's C by doing string s = new A().B.C.
What I'm getting at is, in XAML, these three are the same:
<Style TargetType="{x:Type avalonDock:LayoutAnchorableItem}">
<Setter Property="Title" Value="{Binding Title, Path=Model}"/>
...
<local:PanesStyleSelector.ToolStyle>
<Style TargetType="{x:Type avalonDock:LayoutAnchorableItem}">
<Setter Property="DataContext" Value="{Binding Model}" />
<Setter Property="Title" Value="{Binding Title}"/>
...
<!-- What you currently have -->
<Style TargetType="{x:Type avalonDock:LayoutAnchorableItem}">
<Setter Property="Title" Value="{Binding Model.Title}"/>
...
So really, Model is a property the control is going to look for in it's DataContext, and it will bind to the Title property belonging to Model.
Now the question is, how do you figure out if this is correct? Simple. Navigate through the DataContext. There is a number of ways you can do this, rather you debug, or you use F12; but here is a quick way to figure it out.
We know the target type is LayoutAnchorableItem: TargetType="{x:Type avalonDock:LayoutAnchorableItem}", so we look at its source code and look for the Model property. After looking, you don't see it, but you also notice that LayoutAnchorableItem inherits from LayoutItem, which is also our second target type coincidentally. So we look at LayoutItem source code. As you know it, there is a public object Model property. With that being said, we can make the assumption that through a chain of events, we can figure that our Model property ends up getting set to an instance of FileViewModel or ToolViewModel, and both inherit from PaneViewModel.
With that being said, we know that the DataContext of each Pane in the DockingManager is either a LayoutAnchorableItem or LayoutItem, whose Model is eventually set to an instance of FileViewModel or
toolViewModel.

How to modify legacy named style for having different setters based on targetTypes?

I have this named style
<Style x:Key="validationSupport" TargetType="{x:Type Control}">
<Setter Property="Margin" Value="5,2,14,2" />
...OMISSIS...
<Style.Triggers>
...OMISSIS...
<DataTrigger Binding="{Binding DataContext.ActiveWorkspace.Editable, RelativeSource={RelativeSource AncestorType=Window}}" Value="False">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
I use it extensively for TextBoxes, ComboBoxes, DatePickers etc, so I used as TargetType a super class for all these elements, Control.
Now I would like to differentiate the setter inside the dataTrigger using specific properties that 'Control' doesn't have. It seems I have to create different styles with different names,each for every targetType I want to differentiate, but that way I have to change the style name inside all elements which use it. Is there a smarter way to achieve that goal ? I don't want want to go and modify every xaml file I have.
Update after first answer
I have tried to put the following setters inside the datatrigger:
<Setter Property="Background" Value="#FFECECF8" />
<Setter Property="CheckBox.IsEnabled" Value="False" />
<Setter Property="DatePicker.IsEnabled" Value="False" />
<Setter Property="ComboBox.IsEnabled" Value="False" />
<Setter Property="TextBox.IsReadOnly" Value="True" />
Unfortunately the tests gave odd results. The IsEnabled property is set for TextBoxes too despite the prefix should limit its application to CheckBoxes, DatePickers and ComboBoxes.
My final need was to make some control contents unchangeable avoiding the difficult to read colors associated with disabled controls. From previous researches I understood that changing the colors for a 'disabled' control is not an easy task and involves the redefinition of the control template. So I thought to apply a combination of IsReadOnly and Background, but it is not applicable for the above problem. In fact CheckBoxes, DatePickers and ComboBoxes can only be made unchangeable using the IsEnabled property.
Am I missing something ?
There is a way, but I have to warn you - this is far from best-practice and should be avoided
WPF allows you to use desired type as a prefix for the property. That way, if you apply the style to a control that doesn't inherit from the prefixed type - the setter is ignored.
<Style x:Key="validationSupport" TargetType="{x:Type Control}">
<Setter Property="Margin" Value="5,2,14,2" />
...OMISSIS...
<Style.Triggers>
...OMISSIS...
<DataTrigger Binding="{Binding DataContext.ActiveWorkspace.Editable, RelativeSource={RelativeSource AncestorType=Window}}" Value="False">
<Setter Property="IsEnabled" Value="False" />
<Setter Property="Button.Background" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
[Test this extensively, since I suspect that it might create memory leaks.]

Use GroupSizeReductionOrder in RibbonControlsLibrary with MVVM

I am using the RibbonControlsLibrary (.Net 4.0) for my ribbons. My implementation is completely realized using MVVM.
Now I like to use the benefit of the resizing feature. But I can't set the reduction order of the group boxes. This is because I have to define the names of the group boxes in a special order. I can't set the names of the group boxes, because I am using data templates.
I tried to bind the name of the ribbon group box user controls to a property on the datacontext, but as I supposed this doesn't work.
So, do I have any chance to set the ribbon group box user control names to a property of the data context?
Setting the RibbonGroups' names
There may be others solutions, mine is to implement a Behavior that sets the RibbonGroup's name in the OnAttached method.
public class RibbonGroupNameBehavior : Behavior<RibbonGroup>
{
protected override void OnAttached()
{
base.OnAttached();
GroupeCommandesViewModel groupeCommandesViewModel = this.AssociatedObject.DataContext as GroupeCommandesViewModel;
if (groupeCommandesViewModel != null)
{
this.AssociatedObject.Name = groupeCommandesViewModel.Nom;
}
}
}
You may want to add a try-catch block around the assignment...
Using the behavior
As you are using DataTemplates, I think you'll have to assign the behavior to the RibbonGroups with a Style.
I have been using this solution with success.
As you'll see, my behavior's code does not provide the required dependency property, you'll have to add it.
Here is my shortened Ribbon's style :
<r:Ribbon.Style>
<Style TargetType="{x:Type r:Ribbon}">
<Setter Property="ItemContainerStyle">
<Setter.Value>
<Style TargetType="{x:Type r:RibbonTab}">
<Setter Property="ItemsSource" Value="{Binding ListeCommandes, Converter={StaticResource CommandesToGroupesConverter}}" />
<Setter Property="GroupSizeReductionOrder" Value="{Binding ListeCommandesOrdreRedimensionnement}" />
<Setter Property="ItemContainerStyleSelector">
<Setter.Value>
<ribbonVM:RibbonGroupStyleSelector>
<ribbonVM:RibbonGroupStyleSelector.GenericStyle>
<Style TargetType="{x:Type r:RibbonGroup}" BasedOn="{StaticResource RibbonGroupStyle}" />
</ribbonVM:RibbonGroupStyleSelector.GenericStyle>
</ribbonVM:RibbonGroupStyleSelector>
</Setter.Value>
</Setter>
</Style>
</Setter.Value>
</Setter>
</Style>
</r:Ribbon.Style>
And my RibbonGroup's style :
<Style x:Key="RibbonGroupStyle" TargetType="{x:Type r:RibbonGroup}" BasedOn="{StaticResource RibbonControlStyle}">
<Setter Property="Header" Value="{Binding Libellé}" />
<Setter Property="ItemsSource" Value="{Binding Commandes}" />
<Setter Property="CanAddToQuickAccessToolBarDirectly" Value="False" />
<Setter Property="ihm_behaviors:RibbonGroupNameBehavior.IsEnabled" Value="True" />
</Style>
Databinding the GroupSizeReductionOrder property
If you look at the definition of the GroupSizeReductionOrder property, you'll see it's a StringCollection with a TypeConverter StringCollectionConverter.
From what I have seen, this converter only converts from string to StringCollection.
So your property shoud be either a string or a StringCollection.

Attached behaviors and styles

I use an attached behavior that allows a DoubleClick event to be wired to a command in a view model, as in the binding below:
<ListBox Style="{StaticResource MasterListBoxStyle}"
b:SelectionBehavior.DoubleClickCommand="{Binding EditCommand}"
>
I need multiple list boxes for a presentation, all of which will need a DoubleClick wired to an EditCommand.
Can I push this behavior into my MasterListBoxStyle? How?
Cheers,
Berryl
<Style x:Key="MasterListBoxStyle" TargetType="ListBox">
<Setter Property="ItemsSource" Value="{Binding MasterVm.AllDetailVms}" />
<Setter Property="ItemContainerStyle" Value="{StaticResource MasterListingRowStyle}" />
<Setter Property="IsSynchronizedWithCurrentItem" Value="True" />
<Setter Property="AlternationCount" Value="2" />
</Style>
You should be able to add a simple Setter like so in WPF:
<Setter Property="b:SelectionBehavior.DoubleClickCommand" Value="{Binding EditCommand}" />
Assuming the b xmlns is defined in the XAML file that contains your Style.
This won't work in Silverlight though, since Bindings are not supported in Setters. This is something Microsoft is fixing in Silverlight 5.

Question about Data Template or Style in WPF xaml

I have a textbox that has the following simple XAML (not necessary to read it - just have it for reference):
<TextBox Name="m_ctrlUserDeviceType" Style="{StaticResource textStyleTextBox}" Text="{Binding Source={x:Static api:MySettings.Instance}, Path=UserDeviceType, ValidatesOnExceptions=true, NotifyOnValidationError=true}" Validation.Error="TextBox_Error" MinHeight="25" Margin="4" VerticalAlignment="Top" MaxLength="23" VerticalContentAlignment="Center" HorizontalAlignment="Left" MinWidth="100"></TextBox>
For completeness, the style textStyleTextBox looks like this (again, not necessary to read to answer question):
<Style x:Key="textStyleTextBox" TargetType="TextBox">
<Setter Property="Foreground" Value="#333333" />
<Setter Property="VerticalAlignment" Value="Top" />
<Setter Property="MinHeight" Value="2" />
<Setter Property="MinWidth" Value="100" />
<Setter Property="Margin" Value="4" />
<Setter Property="MaxLength" Value="23" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="HorizontalAlignment" Value="Left" />
<!-- <Setter Property="Binding Source" Value="{x:Static api:MySettings.Instance}"/>
<Setter Property="Binding ValidatesOnExceptions" Value="true" />
<Setter Property="Binding NotifyOnValidationError" Value="true" /> -->
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self},
Path=(Validation.Errors)[0].ErrorContent}" />
</Trigger>
</Style.Triggers>
</Style>
I have a lot of the stuff (MiHeight, Margin, etc.)in the style because I have a lot of these textboxes and they're almost exactly the same. In fact, there's a lot more in common than just the style. The details of the binding to the class MySettings are almost the same. The only difference is which particular property the textbox is binding too. Additionally, I always user TextBox_Error for Validation.Error.
Is there a way to put the binding info in Style or Data Template so I don't have to keep typing it for each textbox?
I would need to be able to assign an individual property (Path) for each textbox, and I suppose I still need the ability to not use any of it for some particular textbox added in the future (that has nothing to do with databinding to MySettings).
Is there a way to put the TextBox_Error part inside of style or DataTemplate? Using Setter Property did not seem to work for me.
I keep mentioning Data Template as I think the answer might have something to do with that based on reading Pro Silverlight 2 in C# 2008. However, I wasn't able to figure it out. I also tried adding stuff to "Style" as you can see from the commented out stuff in that section.
Thanks,
Dave
I dont think that there is a way to do what you are asking. However, I do think that you could go about it a different way.
What I would look into, is creating a custom control that extends TextBox, then create some dependency properties that, when the control is initialised, setup the bindings and error validation.
This way you can use your custom textbox all over your app and control every property, and even style them the same (just change the target type of your style)
HTH

Resources