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.
Related
I have a WPF .NET Core application with a TabControl bound to an ObservableCollection for the TabItems. I would like the TabControl to be hidden when the ObservableCollection becomes empty, and I would like to display another panel in its place. Then, when an item is added back to the ObservableCollection, I want the alternate panel hidden and the TabControl reshown. How would I accomplish this, hopefully in XAML with as little code-behind as possible? I know I can do it in code-behind.
Below is the key section of the app. I have hidden the TabControl, and included a Border control to represent the Panel that I will show when the TabControl is hidden.
<Window x:Class="TabTest.MainWindow"
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:local="clr-namespace:TabTest"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Border Name="emptyTabPanel" Grid.Row="1" BorderBrush="{DynamicResource {x:Static SystemColors.ControlDarkBrushKey}}"
BorderThickness="1,1,1,1" Margin="5,0,5,5" Visibility="Hidden">
</Border>
<TabControl Name="MainTabControl" Visibility="Visible">
<TabControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Label Content="{Binding Header}" MinWidth="60"/>
<Button BorderThickness="0" Background="Transparent" Height="16" Width="15" Margin="15,2,0,0">
<Image Source="images/close.png" Height="8"/>
</Button>
</StackPanel>
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
</Grid>
</Window>
using System.Windows;
using System.Collections.ObjectModel;
namespace TabTest
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private ObservableCollection<TabEntry> tabEntries;
public MainWindow()
{
InitializeComponent();
tabEntries = new ObservableCollection<TabEntry>();
MainTabControl.ItemsSource = tabEntries;
for (int i = 1; i <= 5; i++)
{
tabEntries.Add(new TabEntry { Header = "tab " + i });
}
}
}
public class TabEntry
{
public string Header { get; set; }
}
}
All ItemsControls provide a HasItems property that you can use in a Trigger. In contrast to a DataTrigger on ItemsSource.Count this also works when ItemsSource is not set at all.
<TabControl ...>
<TabControl.Style>
<Style TargetType="TabControl">
<Style.Triggers>
<Trigger Property="HasItems" Value="False">
<Setter Property="Visibility" Value="Collapsed" />
</Trigger>
</Style.Triggers>
</Style>
</TabControl.Style>
...
</TabControl>
You can do it with a DataTriger in a Style. Note that you need to remove Visibility="Visible" or the Setter won't be able to change it.
<TabControl Name="MainTabControl" Background="Red">
<TabControl.Style>
<Style TargetType="TabControl">
<Style.Triggers>
<DataTrigger Binding="{Binding ItemsSource.Count, RelativeSource={RelativeSource Self}}" Value="0">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</TabControl.Style>
<TabControl.ItemTemplate>
<!-- and so on -->
You need a proper view model for this, rather than binding the tabs directly to the collection. That view model would include a HasItems property which you'll bind your TabControl visibility to, and an inverse property - say IsEmpty - which you'll bind the panel's visibility to.
Bind ObservableCollection's events to listen to changes in item count and raise PropertyChanged events for your view model appropriately.
I have implemented the new DialogService as shown in this issue
A New IDialogService for WPF
However, this doesn't explain how to edit the window of the dialog itself, since the NotificationDialog is a UserControl.
I have tried changing it to a Window but then an exception is raised due to not being the root Window.
Any idea how can I change the Window of the dialog?
Since the Title and the Icon is set in DialogViewModelBase, I have tried to add a ResizeMode property as well.
In DialogViewModelBase:
private ResizeMode _resizeMode;
public ResizeMode ResizeMode
{
get => _resizeMode;
set => SetProperty(ref _resizeMode, value);
}
and in NotificationDialogViewModel implementation:
public NotificationDialogViewModel()
{
Title = "Notification";
ResizeMode = System.Windows.ResizeMode.CanMinimize;
CloseDialogCommand = new DelegateCommand(CloseDialog);
}
However it doesn't work as intended.
For others who search for this - the style can be set using prism:Dialog.WindowStyle.
Example from
https://github.com/PrismLibrary/Prism/blob/master/Sandbox/Wpf/HelloWorld/HelloWorld/Dialogs/NotificationDialog.xaml
New link: https://prismlibrary.com/docs/wpf/dialog-service.html#style-the-dialogwindow
<UserControl x:Class="HelloWorld.Dialogs.NotificationDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
Width="300" Height="150">
<prism:Dialog.WindowStyle>
<Style TargetType="Window">
<Setter Property="prism:Dialog.WindowStartupLocation" Value="CenterScreen" />
<Setter Property="ResizeMode" Value="NoResize"/>
<Setter Property="ShowInTaskbar" Value="False"/>
<Setter Property="SizeToContent" Value="WidthAndHeight"/>
</Style>
</prism:Dialog.WindowStyle>
<Grid x:Name="LayoutRoot" Margin="5">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Text="{Binding Message}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Row="0" TextWrapping="Wrap" />
<Button Command="{Binding CloseDialogCommand}" CommandParameter="True" Content="OK" Width="75" Height="25" HorizontalAlignment="Right" Margin="0,10,0,0" Grid.Row="1" IsDefault="True" />
</Grid>
</UserControl>
Other properties can also be set using Setter. For example,
<Setter Property="WindowStyle" Value="None" />
will hide the title bar.
Any idea how can I change the Window of the dialog?
Looking at the code, it looks like you have to implement IDialogWindow and override the default registration to the built-in implementation.
Also, you don't need to inherit from DialogViewModelBase, just implementing IDialogAware suffices.
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
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>
I have created a basic application using Prism & MVVM. So far, it only consists of the Shell, and one View/ViewModel.
During application load, I am loading the View into my main region and this displays on screen. This works, but I cannot get the textbox on the view to focus. It looks like the cursor is in the box (although it's not flashing), but it doesn't accept text input until I click on the textbox.
I've recreated this in a new project, where all I've done is install prism/prism.unityextensions, set up the shell and the view, and loaded the view into the shell region. Neither xaml file has anything in the code behind.
Shell
<Window x:Class="MVVMFocusTest.Shell"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prism="http://www.codeplex.com/prism"
Title="MainWindow" Height="350" Width="525">
<Grid>
<DockPanel LastChildFill="True">
<ContentControl Name="MainRegion" DockPanel.Dock="Top" prism:RegionManager.RegionName="MainRegion" />
</DockPanel>
</Grid>
</Window>
View1
<UserControl x:Class="MVVMFocusTest.View1"
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="300" d:DesignWidth="300">
<StackPanel>
<Grid FocusManager.FocusedElement="{Binding ElementName=Username}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Label Grid.Row="0" Grid.Column="0">Username</Label>
<TextBox Name="Username" Grid.Row="0" Grid.Column="1" ToolTip="Enter Username" TabIndex="0" />
<Label Grid.Row="1" Grid.Column="0">Password</Label>
<PasswordBox Grid.Row="1" Grid.Column="1" Name="LoginPassword" PasswordChar="*" ToolTip="Enter Password" TabIndex="1" />
</Grid>
</StackPanel>
</UserControl>
Can anyone point out what I'm doing wrong? As far as I'm aware, the FocusManager.FocusedElement="{Binding ElementName=Username}" should be sufficient to set the focus.
As per FocusManager documentation -
Logical focus pertains to the FocusManager.FocusedElement within a
specific focus scope.
So, its not necessary that element with logical focus will have keyboard focus as well but vice versa is true i.e. element with keyboard focus will surely have a logical focus as well.
As stated in documentation FocusManager.FocusedElement guarantees logical focus and not keyboard focus. So what you can do is create an attach behaviour similar to FocusManager.FocusedElement which will set keyboard focus on an element.
You can refer to this for setting keyboard focus using attached behaviour - Setting keyboard focus in WPF.
Code from that article -
namespace Invoices.Client.Wpf.Behaviors
{
using System.Windows;
using System.Windows.Input;
public static class KeyboardFocus
{
public static readonly DependencyProperty OnProperty;
public static void SetOn(UIElement element, FrameworkElement value)
{
element.SetValue(OnProperty, value);
}
public static FrameworkElement GetOn(UIElement element)
{
return (FrameworkElement)element.GetValue(OnProperty);
}
static KeyboardFocus()
{
OnProperty = DependencyProperty.RegisterAttached("On", typeof(FrameworkElement), typeof(KeyboardFocus), new PropertyMetadata(OnSetCallback));
}
private static void OnSetCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
var frameworkElement = (FrameworkElement)dependencyObject;
var target = GetOn(frameworkElement);
if (target == null)
return;
frameworkElement.Loaded += (s, e) => Keyboard.Focus(target);
}
}
}
Use in XAML -
<UserControl xmlns:behaviors="clr-namespace:Invoices.Client.Wpf.Behaviors">
<Grid behaviors:KeyboardFocus.On="{Binding ElementName=TextBoxToFocus}">
<TextBox x:Name="TextBoxToFocus" />
</Grid>
</UserControl>
FocusManager.FocusedElement="{Binding ElementName=Username}" sets logical focus but not physical focus.
Physical focus is the normal focus, logical focus is kinda a second focus which is still a little bit buggy in wpf 4.0.
I would suggest you to use Keyboard.Focus(this.Username).