How to 'Disable' a WPF UserControl? - c#

I have a UserControl, and I need to change its appearance when 'IsEnabled' is false. I know this is pretty basic WPF styling, but I can't seem to put the pieces together. I assume that I need to create a Style, and add a trigger for "Property="IsEnabled" Value="False". But where do I put the Style definition? (<UserControl.Resources>?) How do I apply it? ... in the UserControl or in the Parent window? Does my trigger need to be inside a Setter element? Does the style need to be applied to the UserControl or to its children? I don't know what other questions I need to ask!
If you feel this is a duplicate of another question, please direct me to it.
If you know of a good, simple tutorial on WPF styling that would answer my questions, I would be very grateful to hear of it.
My code looks like this:
<UserControl x:Class="UserControls.UCDemo"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:UserControls"
mc:Ignorable="d"
d:DesignHeight="240" d:DesignWidth="200">
<UserControl.Resources>
</UserControl.Resources>
<Grid Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="5*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid Grid.Row="0" Background="{Binding BG}" >
<Ellipse Grid.Row="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="5" Fill="DeepSkyBlue" />
</Grid>
<Grid Grid.Row="1" Background="Tomato" />
</Grid>
<uc:UCDemo x:Name="Demo" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="300,100,0,0" Width="80" Height="100" Style="{StaticResource uc:DemoStyle}"
Visibility="{Binding LightVisible, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Converter={StaticResource BooleanToVisibilityConverter}}" />
Thank you for your help!

It depends on how exactly the appearance should change when IsEnabled is false.
A simple Style with a Trigger could be assigned directly to the UserControl's Style property. The one below just sets the Opacity to 0.5.
<UserControl ...>
<UserControl.Style>
<Style TargetType="UserControl">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" Value="0.5"/>
</Trigger>
</Style.Triggers>
</Style>
</UserControl.Style>
<Grid Background="Transparent">
...
</Grid>
</UserControl>

I'm not sure I get what your question is. However in order to change the IsEnabled property of a control, depending on an other controls is:
Let's suppose that the control which its IsEnabled must be changed, is A and the control which A is depends on, is B.
First, choose a name for B: x:Name = "AccessCheckBox".
Then Write a binding like this for control B:
< ... IsEnabled = {Binding ElementName = "AccessCheckBox", Path="IsChecked".../>
Done. I hope I got what you mean.

Related

WPF - Binding custom DataGridTextColumn template's Content property to parent's property

First of, I am coming back to WPF after several years of leaving it behind, I knew a little of it before and develop a few windows but now I am beyond rusty.
I am trying to build a DataGrid with filter headers, now I understand that there isn't a ready made control for that and needs to be created, which I have started using a Template.
My template consists of a Label control to the left which contains the title of the header, and a DatePick control to the right which I will use as part of my filtering process. I am trying to get Label.Content property inside my template to pick the DataGridTextColumn.Header property value of template parent.
I have tried RelativeSource, TemplatedParent and everything else there is, I also couldn't find any post on here that described a similar issue to mine nor a solution. Any help would be greatly appreciated. Thanks.
My code
<UserControl x:Class="CustomControls.ReportsListingControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="100" d:DesignWidth="600">
<UserControl.Resources>
<Style x:Key="ColumnHeaderStyle1" TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="DataGridColumnHeader">
<Grid Width="200" Height="35">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Grid.Column="0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Width="50"
Content="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGridTextColumn}}, Path=Header}" />
<DatePicker Grid.Column="1"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Width="30"
BorderThickness="0" Text="" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<Grid>
<DataGrid Name="DataGrid1" >
<DataGrid.Columns>
<DataGridTextColumn HeaderStyle="{StaticResource ColumnHeaderStyle1}" Header="The Text I want displayed in my template label" />
</DataGrid.Columns>
</DataGrid>
</Grid>
Bind to the DataContext itself:
<Label ... Content="{Binding}" />
The DataContext of a DataGridColumnHeader is the Header object itself, i.e. the string in your case.

C# WPF: Changing PlacementTarget of a ToolTip

I'm trying to change the PlacementTarget of a ToolTip to a window further up the visual tree in order to have custom ToolTip clipping effects in that window. I've hooked everything up except for the PlacementTarget. Here's an example from XAML and in code...neither work. This style is currently being used for a single tooltip attached to a TextBox.
<Style TargetType="ToolTip">
<Setter Property="ToolTipService.PlacementTarget"
Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type Grid }} }" />
</Style>
If I go into code and look at the tooltip.PlacementTarget once it's attached to something...it's always set to the TextBox. I've tried multiple ways of using the VisualTree to get different UIElements. Nothing seems to work...so I'm assuming I'm not understanding or missing something.
The thing that really gets me is that if I go into my code and look at the PlacementTarget of the tooltip, it won't let me set it to anything else. For instance:
var ancestors = toolTip.PlacementTarget.GetSelfAndAncestors();
foreach(var ancestor in ancestors)
{
if(var ancestor is Grid)
{
// Conditional always hits.
// Before this line, PlacementTarget is a TextBox.
toolTip.PlacementTarget = (UIElement)ancestor;
// After the line, PlacementTarget is still a TextBox.
}
}
What am I doing incorrectly or not understanding?
Edit for Context: The custom clipping effect is to basically just find the closest ancestor window to the ToolTip's target and use that to make sure the ToolTip never goes outside the bounds of that window.
A short sample setting a Tooltip, using a property on the parent Window as PlacementTarget.
<Window x:Class="WpfApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Tag="Bar">
<Window.Resources>
<ToolTip x:Key="FooToolTip">
<StackPanel>
<TextBlock Text="{Binding PlacementTarget.Tag, RelativeSource={RelativeSource AncestorType={x:Type ToolTip}}}"/>
</StackPanel>
</ToolTip>
</Window.Resources>
<Grid>
<TextBlock
Text="Foo"
ToolTip="{StaticResource FooToolTip}"
ToolTipService.PlacementTarget="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
HorizontalAlignment="Center" VerticalAlignment="Center" Height="20" Width="50">
</TextBlock>
</Grid>
</Window>
EDIT
To answer your questions,
the first snippet uses ToolTipService in the wrong way:
The ToolTipService class attached properties are used to determine the placement, behavior, and appearance of a tooltip. These properties are set on the element that defines the tooltip.
Applied in a style:
<Window x:Class="WpfApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
Tag="Bar">
<Window.Resources>
<ToolTip x:Key="FooToolTip">
<StackPanel>
<TextBlock Text="{Binding PlacementTarget.Tag, RelativeSource={RelativeSource AncestorType={x:Type ToolTip}}}"/>
</StackPanel>
</ToolTip>
<Style x:Key="ToolTipStyle">
<Setter Property="ToolTipService.ToolTip" Value="{StaticResource FooToolTip}"/>
<Setter Property="ToolTipService.PlacementTarget" Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}}"/>
</Style>
</Window.Resources>
<Grid>
<TextBlock
Text="Foo" Style="{StaticResource ToolTipStyle}"
HorizontalAlignment="Center" VerticalAlignment="Center" Height="20" Width="50">
</TextBlock>
</Grid>
</Window>
As for your second snippet in code behind, you can't set the PlacementTarget once the ToolTip is open and when the ToolTip is closed the PlacementTarget is null. As #mm8 pointed out, this has to do with the ToolTip and the PlacementTarget being in different visual trees, since a ToolTip spawns
a Window of its own.

Cannot bind to properties outside Data Context

In my application I have a two TreeView objects that are bound to the same data.
I made a user control for the Tree which is called TreeView and looks like this:
<UserControl x:Class="MyApp.Views.TreeControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:model="clr-namespace:MyApp.Model"
xmlns:viewModel="clr-namespace:MyApp.ViewModels"
xmlns:views="clr-namespace:MyApp.Views"
xmlns:converters="clr-namespace:MyApp.Converters">
<UserControl.Resources>
<converters:EnumToPicConverter x:Key="Converter"></converters:EnumToPicConverter>
<!--Control colors.-->
<Style x:Key="MyTreeViewItemStyle" TargetType="TreeViewItem">
<Setter Property="IsExpanded" Value="{Binding Path=(model:TreeNode.IsExpanded), Mode=TwoWay}" />
</Style>
<HierarchicalDataTemplate DataType="{x:Type model:TreeNode}" ItemsSource="{Binding ChildListNodes}">
<HierarchicalDataTemplate.Triggers>
<DataTrigger Binding="{Binding IsEqual}" Value="false">
<Setter Property="TreeViewItem.Background" Value="Blue"></Setter>
</DataTrigger>
</HierarchicalDataTemplate.Triggers>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Path=EntityType,Converter={StaticResource Converter}}" />
<TextBlock Margin="5,0" Text="{Binding ItemName, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"/>
</StackPanel>
</HierarchicalDataTemplate>
</UserControl.Resources>
<TreeView ItemsSource="{Binding RootNode}" ItemContainerStyle="{StaticResource MyTreeViewItemStyle}" />
Now, In my main window, I use it in the following way:
<Window x:Class="MyApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:model="clr-namespace:MyApp.Model"
xmlns:viewModel="clr-namespace:MyApp.ViewModels"
xmlns:views="clr-namespace:MyApp.Views"
xmlns:converters="clr-namespace:MyApp.Converters"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<viewModel:TreeViewModel/>
</Window.DataContext>
<DockPanel>
<Grid DockPanel.Dock="Top" Name="LoadRow">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<views:DbChooser Path="{Binding Path1}" ReloadCommand="{Binding LoadFileACommand}" Grid.Column="0"/>
<views:DbChooser Path="{Binding Path2}" ReloadCommand="{Binding LoadFileBCommand}" Grid.Column="1"/>
</Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<views:TreeControl Grid.Column="0" ItemName="{Binding Name1}"/>
<views:TreeControl Grid.Column="1" ItemName="{Binding Name2}"/>
</Grid>
</DockPanel>
Problem is that:
The DataContext is "viewModel:TreeViewModel" , but I want to bind each treeViewItem's Textblock content to a different property of "model:TreeNode" (which is the type of class of the TreeNodeItem holds..)
And in the main window it says "Cannot resolve property "Name1" in data context of type 'MyApp.ViewModels.TreeViewModel' .
I tried various different options and read few posts here in StackOverFlow but couldn't find a solution..
Thanks for any help.
edit, few clarifications:
1. TreeControl just wraps WPF TreeView (you can see the code is pasted..)
2. The DataContext of The MainWindow is TreeViewModel, it is a class that holds the Root Node of the tree (of type TreeNode) and few other properties I use. The Property that I want to "send" to the TreeControl is a property of type TreeNode, which is the type of the TreeViewItems
Another edit:
In other words, what I want to accomplish is that:
To "tell" to the first TreeControl " Please put in the text block of each TreeNodeItem the content of the the property 'Name1' "
And to "tell" the second TreeControl "Please put in the text block of each TreeNodeItem the content of the property 'Name2' "
I do not try to answer but perhaps give you some hints to debug.
You can use "DataContextChanged" event on most xaml tag to make sure you get the DataContext you expect.
If you have the expected DataContext but got warning in xaml. You could use:
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
and set:
<AnyTagThatHasDataContext d:DataContext="{d:DesignInstance Type=youDataContextObject}">
That will help to design your window/UserControl

Binding in Style in Silverlight 5

I know that Silverlight 5 introduces the data binding in styles. I want to bind the source of image which is present in content template in the style of a button.
I am using the below code where I am trying to set the image source property in style.
// Style
<UserControl x:Class="MGPIControls_Simple.ButtonControl"
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:vsm="clr-namespace:System.Windows;assembly=System.Windows"
Height="40" Width="40"
mc:Ignorable="d" x:Name="ButtonControlSample">
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.Resources>
<Style x:Key="ImageButtonStyle" TargetType="Button">
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<!-- binding in style -->
<Image Source="{Binding ImageSource}"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
Stretch="Fill"/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<Button x:Name="ButtonBase" Style="{StaticResource ImageButtonStyle}"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
</Grid>
Where ImageSource is the dependency property I have created. If I dont bind the image source property and keep it static to some image url the things are working fine but binding is not working. Please let me know where I am wrong in above approach.
You have to use binding like
<TextBlock Text="{Binding Path=DataContext.BusyText, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UserControl}}"
Well, how to put this... what you try to do is not the new Silverlight 5 feature Binding in Styles. This kind of binding is always possible, even with older Silverlight versions.
You have a DataTemplate and that means any binding you declare is evaluated when actual UI elements are instantiated from the template. And your binding Source="{Binding ImageSource}" is evaluated against your Button's DataContext.
If there is no public property ImageSource then your Button won't show any image.

ContentPresenter in UserControl

I'm new to WPF and I'm trying to create an UserControl which will have some nested content.
<my:InformationBox Header="General Information" Width="280">
<StackPanel>
<Label>Label1</Label>
<Label>Label2</Label>
</StackPanel>
</my:InformationBox>
As you can see I want to put a StackPanel into it. As I read some articles I am supposed to add ContentPresenter to my UserControl, so I did, but I cannot find what should be binded to it's Content property.
Here is my UserControl code
<UserControl x:Class="ITMAN.InformationBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="200" d:DesignWidth="280" Name="infoBox" Loaded="infoBox_Loaded">
<StackPanel Width="{Binding ElementName=infoBox, Path=Width}" HorizontalAlignment="Stretch">
<Label Content="{Binding ElementName=infoBox, Path=Header}" />
<Border BorderThickness="0,1,0,0" Padding="10 5" Margin="5 0 5 10" BorderBrush="#B4CEDE">
<StackPanel>
<ContentPresenter Content="{Binding Content}" />
<Label Content="End" />
</StackPanel>
</Border>
</StackPanel>
</UserControl>
I've tried many combinations from various articles, but I cannot find any working example of what I want to achieve.
Similiar question was asked earlier by another user, but given there answers didn't help me: Does anyone have a simple example of a UserControl with a single ContentPresenter?
ContentPresenter is kind of a magic control. If you don't supply anything to it, it will automatically set the Content, ContentTemplate and ContentTemplateSelector property with a TemplateBinding to the TemplatedParent. Which means, you don't need to supply anything to it, just
<ContentPresenter/>
in your UserControl, and it should automatically use the corresponding properties found in your UserControl.
Also remember that a binding like {Binding Content} always referes to your DataContext, which i guess is not what you wanted.
I solved this problem by applaying custom style to GroupBox. I've created Syle in ResourceDictionary, which looks as follows
<Style x:Key="InformationBoxStyle" TargetType="GroupBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="GroupBox">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Label>
<ContentPresenter Margin="4" ContentSource="Header"
RecognizesAccessKey="True" />
</Label>
<Border Grid.Row="1" BorderThickness="0,1,0,0" Padding="10 5"
Margin="5 0 5 10" BorderBrush="#B4CEDE">
<StackPanel>
<ContentPresenter />
</StackPanel>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And applied this style to GroupBox
<GroupBox Header="General Information" Width="280" Style="{StaticResource InformationBoxStyle}">
<StackPanel>
<Label>Label1</Label>
<Label>Label2</Label>
</StackPanel>
</GroupBox>
This code works as expected
You may also refer to this great article, which shows different options to achieve it:
http://www.codeproject.com/Articles/82464/How-to-Embed-Arbitrary-Content-in-a-WPF-Control
It also describes why ContentPresenter doesn't work in my code.
You need to create a dependency property on your UserControl code behind such as InnerContent.
public object InnerContent
{
get { return GetValue(InnerContentProperty); }
set { SetValue(InnerContentProperty, value); }
}
public static readonly DependencyProperty InnerContentProperty =
DependencyProperty.Register("InnerContent", typeof(object), typeof(ConfirmationControl), new PropertyMetadata(null));
Then you need to bind to that InnerContent on the XAML side of that UserControl.
<ContentControl Content="{Binding InnerContent, ElementName=userControl}" />
Then when you use it instead of placing content in the UserControl directly and overwriting the existing content just add it to the InnerContent portion.
<UserControls:InformationBox>
<UserControls:InformationBox.InnerContent>
<TextBlock Text="I'm in the InnerContent" />
</UserControls:InformationBox.InnerContent>
</UserControls:InformationBox>
Otherwise using a Template or Style is just as good but if you're wanting to package up a UserControl for use without forcing anyone to also reference a style or template this is probably one of your better options.

Categories

Resources