My attempt to create a CustomControl like a TextBox with variable caption.
I try to learn create CustomControl and my exceptation is that my CustomControl (I called it TextBoxCustomControl) has every properties and methods of TextBox and also has new property Caption.
I hope that is right, that my TextBoxCustomControl is inherited from TextBox and not from Control.
TextBoxCustomControl.cs
namespace CustomControlProject
{
public class TextBoxCustomControl : TextBox
{
public static readonly DependencyProperty CaptionProperty = DependencyProperty.Register("Caption", typeof(string), typeof(TextBoxCustomControl), new FrameworkPropertyMetadata(String.Empty));
public string Caption
{
get { return (string)GetValue(CaptionProperty); }
set { SetValue(CaptionProperty, value); }
}
static TextBoxCustomControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(TextBoxCustomControl), new FrameworkPropertyMetadata(typeof(TextBoxCustomControl)));
}
}
}
Themes\Generic.xaml (specific design for TextBoxCustomControl) -- there is the TextBox called innerTextBox
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:CustomControlProject">
<Style x:Name="CustomStyle" TargetType="local:TextBoxCustomControl" BasedOn="{StaticResource {x:Type TextBox}}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:TextBoxCustomControl}">
<WrapPanel Orientation="Vertical">
<TextBox x:Name="innerTextBox" />
<Label Content="{TemplateBinding Caption}" />
</WrapPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
In MainWindow.xaml I use my TextBoxCustomControl and specific some properties for it.
<Window x:Class="CustomControlProject.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:CustomControlProject"
Title="MainWindow" Height="350" Width="525">
<WrapPanel>
<local:TextBoxCustomControl Width="287" Background="Yellow" Caption="Fill the nickname, please." />
</WrapPanel>
</Window>
My expectation is that innerTextBox inside my TextBoxCustomControl inherit all properties (such like background, width, etc.), but that is not happened. What I do wrong?
First off, there's a difference between a CustomControl, inheriting from a Control, and an UserControl composed out of several Controls.
The Style you're applying is overriding all the TextBox's properties. When you set a Template, you need to use TemplateBinding for all the properties that you want to bind against later. If not, they will be no longer accessible from the outside.
For what you're trying to achieve, you can skip the Style and change XAML to
<Window x:Class="CustomControlProject.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:CustomControlProject"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<local:TextBoxCustomControl
x:Name="CustomBox"
Width="287" Background="Yellow"
Caption="Fill the nickname, please." />
<Label
Content="{Binding ElementName=CustomBox, Path=Caption}" />
</StackPanel>
</Window>
To make it all-in-one, you'd need to build a UserControl.
First of all, what you are making is a composite control. So, inheriting it from TextBox doesn't make any sense. Inheriting from TextBox would make sense if you are improving a normal TextBox with additional capabilities.
Your thinking that whatever you set at your custom control level will be inherited by child controls is wrong. If you want to do that, use TemplateBinding for individual properties. Eg; <TextBox x:Name="innerTextBox" Width="{TemplateBinding Width}"/> .
Note : Some properties (FontSize, FontFamily) are propagated anutomatically without any extra work.
What you are trying to make is already present as <HeaderedContentControl/> . You can study its source code here or using ILSpy.
Sample :
<HeaderedContentControl>
<HeaderedContentControl.Header>
<Border>
<TextBlock Text="Name please !"/>
</Border>
</HeaderedContentControl.Header>
<HeaderedContentControl.Content>
<TextBox />
</HeaderedContentControl.Content>
</HeaderedContentControl>
Related
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.
I am getting really frustrated in trying to achieve a very trivial thing (or at least, something what I would expect should be trivial...)
I have a requirement where a toggle button should be customized, for which I need to make a user control which hosts the toggle button, and host the content which is described in that user control. I made a small mini app to demonstrate the "requirement".
<local:MyUserControl1>
<TextBlock>Just an example</TextBlock>
</local:MyUserControl1>
The MyUserControl1 looks as follows:
<UserControl
x:Class="App2.MyUserControl1"
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"
mc:Ignorable="d" Name="Bla" d:DesignHeight="300" d:DesignWidth="400">
<UserControl.Resources>
<Style TargetType="ToggleButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<Ellipse Width="300" Height="300" Fill="Blue"/>
<ContentPresenter Content="{Binding ElementName=Bla, Path=MainContent}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<ToggleButton/>
</UserControl>
Code behind:
public static DependencyProperty MainContentProperty = DependencyProperty.Register(
"MainContent",
typeof(object),
typeof(MyUserControl1),
null);
public object MainContent
{
get => GetValue(MainContentProperty);
set => SetValue(MainContentProperty, value);
}
When I run the app, the text is shown, but the style / togglebutton is ignored / not applied / whatever.
The visual tree confirms that I do something wrong:
I have viewed many many other related SO Q&As, but I still have no idea how to get this working the way I want.
You code should be working, except that there are no lines shown where ContentPropertyAttribute should be. Could you make sure that MyUserControl1 has it's content property identified and see if that helps.
[ContentProperty(Name = "MainContent")]
public sealed partial class MyUserControl1 : UserControl
...
Update
There is full code below that was tested with Win 10 Pro 1803, build 17134, NETCore 6.2.2.
Note that you can define control template either in UserControl.Resources or external resources to separate it from the "main" UI layout or keep it in ToggleButton.Template for a few less lines of XAML.
UserControlWithContent.xaml
<UserControl
x:Class="SmallTests2018.UserControlWithContent"
x:Name="Self"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ToggleButton>
<ToggleButton.Template>
<ControlTemplate>
<Grid>
<Ellipse Width="300" Height="300" Fill="Blue"/>
<ContentPresenter
HorizontalAlignment="Center"
VerticalAlignment="Center"
Content="{Binding MainContent, ElementName=Self, FallbackValue='{}{ content }'}" />
</Grid>
</ControlTemplate>
</ToggleButton.Template>
</ToggleButton>
</UserControl>
UserControlWithContent.xaml.cs
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Markup;
namespace SmallTests2018
{
[ContentProperty(Name = "MainContent")]
public sealed partial class UserControlWithContent : UserControl
{
public UserControlWithContent()
{
this.InitializeComponent();
}
public static DependencyProperty MainContentProperty =
DependencyProperty.Register("MainContent", typeof(object), typeof(UserControlWithContent), null);
public object MainContent
{
get => GetValue(MainContentProperty);
set => SetValue(MainContentProperty, value);
}
}
}
UserControlWithContentPage.xaml
<Page
x:Class="SmallTests2018.UserControlWithContentPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:SmallTests2018"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Viewbox>
<local:UserControlWithContent>
<TextBlock FontSize="32" Foreground="Yellow">Just an example</TextBlock>
</local:UserControlWithContent>
</Viewbox>
</Page>
Page XAML designer screenshot
I'm trying to institute nested ViewModels in my already working application which uses nested views. Here's an example of what I want to do:
MainWindow View:
<Window x:Name="FCTWindow" x:Class="CatalogInterface.MainWindow"
xmlns:local="clr-namespace:CatalogInterface"
xmlns:vm="clr-namespace:CatalogInterface.ViewModels"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="532">
<Window.Resources>
<vm:MainWindowViewModel x:Key="ViewModel" />
</Window.Resources>
<Grid DataContext="{Binding Path=ViewModel.DirFilesListBoxViewModel}" x:Name="BodyGridLeft" Grid.Row="0" Grid.Column="0">
<local:ctlDirFilesListBox>
<!--
Need to access the `ItemsSource="{Binding }"` and
`SelectedItem="{Binding Path=}"` of the ListBox in
`ctlDirFilesListBox` view -->
</local:ctlDirFilesListBox>
</Window>
Child View:
<UserControl x:Class="CatalogInterface.ctlDirFilesListBox"
xmlns:local="clr-namespace:CatalogInterface"
xmlns:vm="clr-namespace:CatalogInterface.ViewModels"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid x:Name="MainControlGrid">
<ListBox SelectionChanged="ListBoxItem_SelectionChanged"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="#FFFFFF"
Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="3" BorderThickness="0">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}" BasedOn="{StaticResource {x:Type ListBoxItem}}">
<EventSetter Event="MouseDoubleClick" Handler="ListBoxItem_MouseDoubleClick"/>
<EventSetter Event="KeyDown" Handler="ListBoxItem_KeyDown"/>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
</Grid>
</UserControl>
MainWindowViewModel
using System;
using System.Text;
namespace CatalogInterface.ViewModels
{
class MainWindowViewModel
{
public DirFilesViewModel DirFilesViewModel { get; set; }
public MainWindowViewModel()
{
DirFilesViewModel = new DirFilesViewModel();
}
}
}
So, I need to hook ListBox.SelectedItem and ListBox.ItemSource to bind with properties in MainWindowViewModel.DirFilesViewModel. The catch is I have to do the binding in MainWindow View not ctlDirListBox view.
How do i access elements inside my child view? I think that's my biggest barrier. I think all my data context is right, I just can't wrangle the child view elements.
I'm assuming that DirFilesViewModel is the viewmodel for that usercontrol. If that's not the case, let me know what the real situation is and we'll get it sorted out.
This is a very simple case. #JamieMarshall If the XAML you provided is all there is to your UserControl, maybe it shouldn't be a usercontrol at all. You could just write a DataTemplate with that XAML in it and use that, or you could write a Style for ListBox. If you need the events, then a UserControl makes sense, but you may not actually need the events.
But it could just be a minimal example to understand how UserControls are used, and for that purpose it's well suited.
You can assign an instance of your main viewmodel to the main window's DataContext in the window's constructor,
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel();
}
or in the XAML as
<Window.DataContext>
<vm:MainWindowViewModel />
<Window.DataContext>
Neither is particularly preferable, just don't do either one in a UserControl. Your main window is pretty much the only time a view (a window is a view, properly considered) should create its own viewmodel.
Making it a resource doesn't add anything. Your binding to Grid.DataContext is a bad idea -- it's rare that you ever bind anybody's DataContext to anything; this is related to what Will was talking about in your other question -- but even if it were a good idea, this is what the binding would look like:
<Grid
DataContext="{Binding Source={StaticResource ViewModel}}"
>
But don't do that!
One thing you can do to display that usercontrol with the correct data is create "implicit datatemplates" for your your viewmodels that'll be displayed in parents like this one.
For example:
App.xaml
<!-- No x:Key, just DataType: It'll be implicitly used for that type. -->
<DataTemplate DataType="{x:Type vm:DirFilesViewModel>
<local:ctlDirFilesListBox />
</DataTemplate>
Then in MainWindow.xaml:
<UserControl
Grid.Row="0"
Grid.Column="0"
Content="{Binding DirFilesViewModel}"
/>
XAML will go to the window's DataContext for a property named DirFilesViewModel. What it finds there is an object that's an instance of the class also named DirFilesViewModel. It knows it has a DataTemplate for that class, so it uses that datatemplate.
This is amazingly powerful: Imagine you have an ObservableCollection<ViewModelBase> with thirty instances of ten different kinds of viewmodels with different views, and the user selects one or another. The selected viewmodel is in a mainviewmodel property named SelectedChildVM. Here's the XAML to display SelectedChildVM with the correct view:
<ContentControl Content="{Binding SelectedChildVM}" />
That's it.
Moving along:
<!--
Need to access the `ItemsSource="{Binding }"` and
`SelectedItem="{Binding Path=}"` of the ListBox in
`ctlDirFilesListBox` view -->
No you don't! That's the last thing you want to do! Some UserControls have properties of their own, instead of a viewmodel. With those, you bind the properties in the parent like any control.
This is a different use case of UserControls: It's "parameterized" by inheriting a viewmodel as its DataContext. The information you give it is the viewmodel.
The controls in the UserControl should have their own bindings, where they get that stuff from properties of the UserControl's viewmodel.
Let's assume the usercontrol's viewmodel (I'm guessing that's what DirFilesViewModel is) has a Files property (ObservableCollection<SomeFileClass>) and a SelectedFile class (SomeFileClass). You likely don't need ListBoxItem_SelectionChanged.
<UserControl x:Class="CatalogInterface.ctlDirFilesListBox"
xmlns:local="clr-namespace:CatalogInterface"
xmlns:vm="clr-namespace:CatalogInterface.ViewModels"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid x:Name="MainControlGrid">
<ListBox
ItemsSource="{Binding Files}"
SelectedItem="{Binding SelectedFile}"
SelectionChanged="ListBoxItem_SelectionChanged"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Background="#FFFFFF"
Grid.Row="2"
Grid.Column="1"
Grid.ColumnSpan="3"
BorderThickness="0"
>
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}" BasedOn="{StaticResource {x:Type ListBoxItem}}">
<EventSetter Event="MouseDoubleClick" Handler="ListBoxItem_MouseDoubleClick"/>
<EventSetter Event="KeyDown" Handler="ListBoxItem_KeyDown"/>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
</Grid>
</UserControl>
How do i access elements inside my child view?
You could add two dependency properties (for example named ItemsSource and SelectedItem) to the code-behind class of your ctlDirFilesListBox control and bind to these in the parent window:
<local:ctlDirFilesListBox ItemsSource="{Binding Property}" SelectedItem="{Binding Property}" />
You should also bind to these properties in the UserControl:
<ListBox SelectionChanged="ListBoxItem_SelectionChanged"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="#FFFFFF"
Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="3" BorderThickness="0"
ItemsSource="{Binding ItemsSource, RelativeSource={RelativeSource AncestorType=UserControl}}"
SelectedItem="{Binding SelectedItem, RelativeSource={RelativeSource AncestorType=UserControl}}">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}" BasedOn="{StaticResource {x:Type ListBoxItem}}">
<EventSetter Event="MouseDoubleClick" Handler="ListBoxItem_MouseDoubleClick"/>
<EventSetter Event="KeyDown" Handler="ListBoxItem_KeyDown"/>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
public class ctlDirFilesListBox : UserControl
{
//...
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(ctlDirFilesListBox));
public IEnumerable ItemsSource
{
get { return (IEnumerable)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public static readonly DependencyProperty SelectedItemProperty =
DependencyProperty.Register("ItemsSource", typeof(object), typeof(ctlDirFilesListBox));
public object SelectedItem
{
get { return GetValue(SelectedItemProperty); }
set { SetValue(SelectedItemProperty, value); }
}
}
I would like to bind a property from a nested UserControl to my MainWindow.
I'm already using a similar structure for other things but this is a bit different:
-MainWindow
--Ribbon
---RibbonButton <-- the button I would like to disable
--/Ribbon
--UserControl1 - not directly the control I want bind to
---UserControl2 - nested user control, sitting inside the above one
----TextBox <-- Bind the RibbonButton to the Text property. Disable the button when TextBox empty
---/UserControl2
--/UserControl
-/MainWindow
So, the usual combination of Elementname and Path doesn't work here obviously. Is DataContext the way to go here? The UserControls are in separate files and so is the MainWindow. Or should I do this in code-behind?
Some of the code I'm having there:
MainWindow
<RibbonWindow.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVis"/>
</RibbonWindow.Resources>
...
<RibbonGroup Header="Import">
<RibbonButton x:Name="mplImpFromForum" Label="From forum" FontSize="12" LargeImageSource="Icons/General/Icon8_download_72px.png" Click="mplImpFromForum_Click"/>
<RibbonButton x:Name="mplImpManual" Label="Manual import" SmallImageSource="Icons/General/Icon8_whole_hand_32px.png"}>
<RibbonButton.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=frmLogin, Path=}"
// here lies my problem
</Style.Triggers>
</Style>
</RibbonButton.Style>
</RibbonButton>
</RibbonGroup>
</RibbonTab>
</Ribbon>
<Viewbox Grid.Row="1" Panel.ZIndex="1" MaxHeight="200" MaxWidth="200">
<local:frmLogin x:Name="frmLogin" Loaded="frmLogin_Loaded" Grid.Row="1"/>
</Viewbox>
<local:frmMPLMain x:Name="frmMPLMain" Grid.Row="1"/> // "Source UserControl, "parent
to the real target.
The first nested UserControl
<UserControl x:Class="Fever_Tool_WPF.frmMPLMain"
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:Fever_Tool_WPF"
mc:Ignorable="d"
x:Name="MPLRoot"
d:DesignHeight="500" d:DesignWidth="1000">
...
<local:frmMPLImp x:Name="frmMPLImp" Grid.Column="2" Grid.ColumnSpan="4" Grid.RowSpan="6"/>
// The second UserControl, this one contains the TextBox I want to take as a source
<DataGrid HeadersVisibility="None" Grid.RowSpan="3" Grid.ColumnSpan="2"></DataGrid>
</Grid>
The second UserControl
<UserControl x:Class="Fever_Tool_WPF.frmMPLImp"
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:Fever_Tool_WPF"
mc:Ignorable="d"
d:DesignHeight="500" d:DesignWidth="1000">
<UserControl.Resources>
<Style x:Key="uiMPLImpBaseStyle" TargetType="{x:Type FrameworkElement}">
<Setter Property="Margin" Value="5"/>
</Style>
</UserControl.Resources>
...
<TextBox x:Name="txtMPLImport" Style="{StaticResource uiMPLImpBaseStyle}" Grid.Row="2" Grid.ColumnSpan="4"/>
// This is the textbox I would like to check.
...
</Grid>
I have a user control that has a text box and a button.
I want to disable the text box using trigger ( I know how to do this via code)
The XAML is as follow:
<UserControl x:Class="MyProject.UserControl1"
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:l="clr-namespace:MyProject"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Resources>
<Style TargetType="l:UserControl1" >
<Style.Triggers>
<Trigger Property="l:UserControl1.IsEditing" Value="True">
<Setter Property="IsEnabled" Value="False"></Setter>
</Trigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Button Content="Button" HorizontalAlignment="Left" x:Name="button1" VerticalAlignment="Top" Width="75" Grid.Row="0" Click="button1_Click" />
<TextBox Height="23" HorizontalAlignment="Left" x:Name="textBox1" VerticalAlignment="Top" Width="120" Grid.Row="1"/>
</Grid>
</UserControl>
The code is:
using System;
using System.Windows;
using System.Windows.Controls;
namespace MyProject
{
/// <summary>
/// Interaction logic for UserControl1.xaml
/// </summary>
public partial class UserControl1 : UserControl
{
public static readonly DependencyProperty IsEditingProperty = DependencyProperty.Register(
"IsEditing", typeof(Boolean), typeof(UserControl), new PropertyMetadata(false));
public Boolean IsEditing
{
get { return (Boolean)GetValue(IsEditingProperty); }
set { SetValue(IsEditingProperty, value); }
}
public UserControl1()
{
InitializeComponent();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
IsEditing = !IsEditing;
}
}
}
But this setup disable both TextBox and button. How can I only disable the button? If I have several Textbox and I want only some of them are disabled, what is the best option? If I have several different UIElements (such as textbox, calandar, datagrid and .. and I want to disable all of them using one triger, what should I do?
Try moving the style down to your grid, and set the TargetName to textBox1. See the answer to this question for an example: Triggers Based on Properties from DataContext
Btw, you should be able to bind the value of IsEditing directly to textBox1.IsEnabled (warning: coding in-place, so code may not work as-is)
<Button IsEnabled="{Binding Path=IsEditing RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl1}}} />
Set the TargetName of the Setter to the name of the button.
best option is group them and and then disable in one go.
in your code, you havent specified the x:Key for your style, if you dont specify the key, it tries to use it as default style for all controls type of UserControl1. and then you can attach that style to your child controls in your UserControl1:
for Key:
<Style x:Key="styleDefault" TargetType="Control">
attach style to your child controls:
<Button Style="{StaticResource styleDefault}"></Button>
I think the problem here is that you are using a EventTrigger, it is the only trigger that can be set directly on a style, not using template. AFAIK if you use this kind of trigger you can only set properties of the object that fired the event.