WPF set default Visibility value when editing - wpf

I have Visibility bound to a bool, which works perfectly. However when editing the page the Border is not visible. I have to delete the Visibility Binding, make my changes and redo the Visibility Binding.
I'm pretty sure I saw there is a way to set a "editing default", but I cannot find that link anymore (or remember what it was called). Can someone explain how to set the default to visible so I can see it whilst editing, but not affect it's operation at runtime?
<Border Grid.Column="2" BorderBrush="HotPink" BorderThickness="2" MinHeight="100" MinWidth="100"
Visibility="{Binding ElementName=GenerateWorkOrders, Path=IsChecked, Converter={StaticResource booleanToVisibility}, UpdateSourceTrigger=PropertyChanged}">
<Label Content="Not Visible While Editing"/>
</Border>

The problem is that the default value of IsChecked of GenerateWorkOrders CheckBox is false
If IsChecked have Binding, you can use FallbackValue:
<CheckBox x:Name="GenerateWorkOrders" IsChecked="{Binding SomeProperty, FallbackValue=True}" />
Another way is to avoid the binding, you can use the DesignerProperties.IsInDesignMode Attached Properties that indicate if you in design mode (More inforamtion).
You can use this property in behavior, or in XAML only approach:
<Border Grid.Column="2" BorderBrush="HotPink" BorderThickness="2" MinHeight="100" MinWidth="100">
<Border.Style>
<Style TargetType="{x:Type Border}">
<Setter Property="Visibility" Value="{Binding ElementName=GenerateWorkOrders, Path=IsChecked, Converter={StaticResource booleanToVisibilityConverter}, UpdateSourceTrigger=PropertyChanged}" />
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=(componentModel:DesignerProperties.IsInDesignMode)}" Value="true">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<Label Content="Not Visible While Editing"/>
</Border>

Related

Binding detached issue when using a ControlTemplate

I have a listbox which shows some labels
and then multiple (theoretically n, practically up to 6) listboxes which each show some values. Each of those n listboxes is its own view/viewmodel.
Now im changing a Property "HasChanged" in my Value-class inside the list box-ViewModel. I have a binding from "HasChanged" to "IsSelected". There is a List of instances of the value-class in each ViewModel.
Additionally, I have a trigger that listens to "IsSelected"-changes.
Part of the trigger is to apply a new control template.
Inside that Template, I define some styling to make the state visible to the user.
Now here is the problem:
I am using "ValidatesOnExceptions", to make the invalidity of values visible to the user.
Here is the code of the value property:
public string Content
{
get
{
if (!string.IsNullOrEmpty(modifiedValue))
return modifiedValue;
return content;
}
set
{
modifiedValue = value;
HasChanged = (modifiedValue != content);
if (!string.IsNullOrEmpty(interpretAs))
Validate();
OnPropertyChanged(nameof(Content));
}
}
Since I'm setting the "HasChanged"-property thus triggering the selection-control template application, the control template sets a new TextBox with a border.
Now when I'm doing the validation, since the textbox-control has been changed, the binding is detached.
I get the following message:
Note: the problem only occurs when altering IsChanged and Validating with an error in the same setter-call.
I have noticed I could maybe do a workaround, using lots of style triggers instead of the control template. But there's still hope.. im also running into focus issues again when I cant use the control template.
This is the XAML of my Grid.Resources:
<ControlTemplate x:Key="SelectedTemplate" TargetType="{x:Type ListBoxItem}">
<Border BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="Gray" Background="LightBlue" HorizontalAlignment="Stretch" Padding="{TemplateBinding Padding}" Margin="{TemplateBinding Margin}" >
<TextBox Text="{Binding Content, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnExceptions=True}" Margin="0" Padding="0" BorderThickness="0" Background="Transparent" Initialized="TextBox_Initialized" ToolTip="{Binding InterpretAs}" ToolTipService.InitialShowDelay="0"/>
</Border>
</ControlTemplate>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Focusable" Value="False"/>
<Setter Property="IsSelected" Value="{Binding Mode=TwoWay, Path=HasChanged}"/>
<Style.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="True" />
</MultiTrigger.Conditions>
<Setter Property="Template" Value="{StaticResource SelectedTemplate}" />
<!--<Setter Property="Border.BorderThickness" Value="1"/>
<Setter Property="Border.BorderBrush" Value="Gray"/>
<Setter Property="TextBox.Background" Value="Green"/>
<Setter Property="TextBox.HorizontalAlignment" Value="Stretch"/>-->
</MultiTrigger>
</Style.Triggers>
</Style>
<DataTemplate x:Key="ListBoxDataTemplate">
<TextBox Text="{Binding Content, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnExceptions=True}" BorderThickness="0" Margin="0" Padding="0" Background="Transparent" ToolTip="{Binding InterpretAs}" ToolTipService.InitialShowDelay="0" />
</DataTemplate>
And this is the XAML for one of the listboxes:
<ScrollViewer Grid.Row="3" Grid.Column="1" Grid.ColumnSpan="6" Scroll:ScrollSynchronizer.HorizontalScrollGroup="H0" Scroll:ScrollSynchronizer.VerticalScrollGroup="V0" VerticalScrollBarVisibility="Hidden">
<ListBox Grid.Row="3" Grid.Column="1" Grid.ColumnSpan="6" ItemsSource="{Binding PlatformValues}" ItemTemplate="{StaticResource ListBoxDataTemplate}" PreviewMouseWheel="ListBox_PreviewMouseWheel" SelectionMode="Extended" HorizontalContentAlignment="Stretch" FocusManager.IsFocusScope="True">
<ListBox.Style>
<Style TargetType="{x:Type ListBox}">
<Style.Triggers>
<DataTrigger Binding="{Binding PlatformValues.Count, FallbackValue=0, TargetNullValue=0}" Value="0">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</ListBox.Style>
</ListBox>
</ScrollViewer>
I have created a sample.
Try clicking a textbox then removing the last character. So far so good. Now try to set it to a string starting with "x" to trigger validation error, on the same textbox thats already been changed, there will be a red border indicating validation error, thats the desired behavior.
Now set a yet unchanged TextBox directly to "x", the desribed problem will be reproduced. Path to the sample project : LINK REMOVED - SEE ANSWER
Any help on this is appreciated.
The answer is to use a DataTemplate trigger instead of a ControlTemplate
In the DataTemplate I added a border with default background transparent.
<DataTemplate x:Key="ListBoxDataTemplate">
<Border x:Name="ContentTextBoxBorder" BorderThickness="0" BorderBrush="Transparent" Background="Transparent" HorizontalAlignment="Stretch" Margin="-3 0">
<TextBox x:Name="ContentTextBox" Text="{Binding Content, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnExceptions=True}" BorderThickness="0" Margin="0" Padding="0" Background="Transparent" ToolTip="{Binding InterpretAs}" ToolTipService.InitialShowDelay="0" />
</Border>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding
RelativeSource={RelativeSource
Mode=FindAncestor,
AncestorType={x:Type ListBoxItem}},
Path=IsSelected}" Value="True">
<Setter TargetName="ContentTextBoxBorder" Property="Background" Value="LightGreen"/>
<Setter TargetName="ContentTextBoxBorder" Property="BorderBrush" Value="Gray"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
I set it to the desired color when "IsSelected" changes for an item. Working flawlessly.
To #Il Vic - if you're unable to help, keep your comments.

Bind header textblock isEnabled to parent Groupbox isEnabled

Referencing to this question: WPF Databinding: How do I access the "parent" data context?
I wanna do something similiar, but for the header of a Groupbox (because the header does not concern with the Box is being disabled and thus is always black while the rest is light gray. This looks a bit strange to me if all the content of the box is gray, the above is gray, but the box title itself stays black.
So I tried to use the approach mentioned in the linked question by flq to simply bind the isEnabled property of the header textblock to the isEnabled property of the groupbox but it seems that my binding at some point fails and I don't know where and why exactly.
heres my current code:
<GroupBox Header="Change Steps" Grid.Row="2" Grid.ColumnSpan="3" Name="gbChangeSteps">
<GroupBox.Style>
<Style TargetType="GroupBox">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Text="{Binding}" FontWeight="Bold" Height="19" Foreground="Black" IsEnabled="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type GroupBox}}, Path=isEnabled}"/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupBox.Style>
<!-- ... (some non relevant Content)-->
</GroupBox>
after additional research I found the post Disable groupBox including the groupBox name in WPF
that lead me, in combination with Properties->Create Databinding->Binding type->UIElement to the solution that fixed both problems, the one this question was about and the original one that lead to entire restyling, which was that letters like the small g got messed up in the header.
This is the code that fixed the issue:
<GroupBox.Style>
<Style TargetType="{x:Type GroupBox}">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Text="{Binding}" FontWeight="Bold" Height="19" IsEnabled="{Binding IsEnabled, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UIElement}}}">
<TextBlock.Style>
<Style>
<Style.Triggers>
<Trigger Property="Control.IsEnabled" Value="False">
<Setter Property="Control.Foreground" Value ="#FF6D6D6D" />
</Trigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupBox.Style>

How to set the Control Visibilty of the Second Control depending on the Binding Value of the first

I have a Custom Wpf Control i.e. combobox:WpfTwComboBox. I want to set the visibility using a property called DisableProviderSelector.
The usual use of triggers is not helping. The scenario here is when the above control i.e. WindowsFormsHost is made visible or collapsed, I want the opposite to happen to the below custom control.
<StackPanel Grid.Row="3" Grid.Column="2" Height="25" Orientation="Horizontal"
Width="375" HorizontalAlignment="Left">
<WindowsFormsHost Height="25" Width="375">
<WindowsFormsHost.Style>
<Style TargetType="WindowsFormsHost">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=DisableProviderSelector}" Value="true">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=DisableProviderSelector}" Value="false">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</WindowsFormsHost.Style>
<commonControls:ProviderSelectorControl RequiredLevel="Save" ModifiedByUser="providerSelectorControl1_ModifiedByUser" x:Name="providerSelectorControl1"/>
</WindowsFormsHost>
<combobox:WpfTwComboBox x:Name="PortalProviderSelector"
SelectedValue="{Binding SelectedPortalProvider}"
ItemsSource="{Binding Path=PortalProvidersCollection}"
DisplayMemberPath="FullName" Width="350" Height="25"
RequiredLevelFlag="Save">
</combobox:WpfTwComboBox>
</StackPanel>
Can anyone please help me on how to set the visibility here?
So DisableProviderSelector is a bool when set to True WindowsFormsHost needs to be Collapsed and ComboBox needs to be Visible. Reverse when bool is false.
So as far as the ComboBox is concerned if bool is True it's Visible and when False it's Collapsed. Thus just bind the ComboBox directly to the Property and use a BooleantoVisibilityConverter
xaml:
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
</Window.Resources>
...
<combobox:WpfTwComboBox x:Name="PortalProviderSelector"
Width="350"
Height="25"
DisplayMemberPath="FullName"
ItemsSource="{Binding Path=PortalProvidersCollection}"
RequiredLevelFlag="Save"
Visibility="{Binding DisableProviderSelector,
Converter={StaticResource BooleanToVisibilityConverter}}"
SelectedValue="{Binding SelectedPortalProvider}" />

XAML trigger template to set visibilty based on another element

I have several StackPanels that change visibility based on ToggleButtons. The code below works if I replace Tag with btn1 on the DataTrigger-lines.
How do I use the value of the Tag property?
<Window x:Class="MyTestApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="TestApp">
<Window.Resources>
<Style x:Key="panelStyle" TargetType="{x:Type StackPanel}">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=Tag, Path=IsChecked}" Value="False">
<Setter Property="StackPanel.Visibility" Value="Collapsed" />
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=Tag, Path=IsChecked}" Value="True">
<Setter Property="StackPanel.Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<WrapPanel>
<ToggleButton Content="One" Name="btn1" />
<ToggleButton Content="Two" Name="btn2" />
<StackPanel Style="{StaticResource panelStyle}" Tag="{Binding btn1}">
<Label Content="Data to panel 1" />
</StackPanel>
<StackPanel Style="{StaticResource panelStyle}" Tag="{Binding btn2}">
<Label Content="Data to panel 2" />
</StackPanel>
</WrapPanel>
</Window>
This question is very similar, but I'm missing details on how to pass an element name.
XAML - Generic textbox stylewith triggers / parameters?
Your bindings are incorrect.
In your DataTemplate the bindings should be:
<DataTrigger Binding="{Binding Path=Tag.IsChecked, RelativeSource={RelativeSource Self}}" Value="False">
<Setter Property="StackPanel.Visibility" Value="Collapsed" />
</DataTrigger>
Here the RelativeSource with a mode of Self tells the binding engine that the object to bind against is object to which the style is being applied (e.g. your StackPanel). The PropertyPath of Tag.IsChecked tells the binding engine to look for a property called IsChecked from the object stored in Tag.
Finally the bindings in your StackPanel should be:
<StackPanel Style="{StaticResource panelStyle}" Tag="{Binding ElementName=btn1}">
<Label Content="Data to panel 1" />
</StackPanel>
Here ElementName creates a binding to another element in the logical tree. If you do not explicitly assign to any properties in a Binding as in your original example:
Tag="{Binding btn1}"
The value specified is assigned to the Path property. So this would be the same as:
Tag="{Binding Path=btn1}"
Also note, that using Tag is not considered best practice since it's type is of object and its use is unrestricted, and hence can take on any number of different meanings throughout your project (which often makes it difficult to understand, especially when used in Templates that are located far away from their actual use).
Hope this helps!
Use Converter: set the visibility of StackPanel:
<StackPanel Visivility="{Binding IsChecked, ElementName=btn1, Converter={StaticResource BooleanToVisibilityConverter}}">
...
</StackPanel>

Binding the background colour of a control using a trigger in WPF/XAML

Okay, first off I have no experience of WPF whatsoever so please bear with me and apologies if my terminology is a little wayward... ;)
The following code snippet is part of a WPF application that I have inherited. The trigger governs whether mandatory fields on a particular form are highlighted or not. The code works but the highlighting seems to apply to the control and the border (??) which contains it.
<ItemsControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cal="clr-namespace:Caliburn.PresentationFramework.ApplicationModel;assembly=Caliburn.PresentationFramework"
x:Class="company.product.Jobs.JobParametersEditor"
IsTabStop="False">
<ItemsControl.ItemTemplate>
<DataTemplate>
<DockPanel MinHeight="30">
<TextBlock Text="{Binding DisplayName, Mode=OneWay}"
DockPanel.Dock="Left"
VerticalAlignment="Center"
MinWidth="120"
Margin="6,0" />
<Border>
<Border.Style>
<Style TargetType="{x:Type Border}">
<Setter Property="Background"
Value="{x:Null}" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsValid}"
Value="False">
<Setter Property="Background"
Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<ContentControl cal:View.Model="{Binding ValueEditor}"
ToolTip="{Binding ToolTip}"
IsTabStop="False"
MinHeight="19"
VerticalAlignment="Center"
HorizontalAlignment="Stretch" />
</Border>
</DockPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The result is a bit clunky so I would like to restrict the highlighting to the control only but I can't figure out how to do it. I've tried moving the trigger so that it applies to the ContentControl instead of the Border but that didn't work and fiddling about with border margins, padding and thickness hasn't had any effect either.
Could anybody enlighten me as to how to accomplish this?

Resources