I have a UserControl that I'm writing that will have a TextBox and Button. When the user clicks the button, I'd like the current date/time to be placed into the TextBox. The code below has two issues:
For some reason the binding is throwing an exception "Two-way binding requires Path or XPath".
If I just set the value to something like "Test", then it works but only while the button itself is pressed, once I lift the mouse button the text goes away.
In essence, I'd like the value to be set at mouse click not during. I'd like to keep this pure xaml if possible, but I understand if it needs to be in the code-behind. Any help would be appreciated!
<Button x:Name="ClockGo">
<Image Source="/Best.Controls;component/Resources/clock_go.png" />
</Button>
<TextBox HorizontalAlignment="Stretch">
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=ClockGo, Path=IsPressed}" Value="True">
<Setter Property="Text" Value="{Binding Source={x:Static sys:DateTime.Today}, StringFormat='{}{0:MM/dd/yyyy}'}" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
You should be able to get rid of "Two-way binding requires Path or XPath" exception by setting the binding mode to OneWay. Textbox has a two way binding by default and I believe the exception is due to the value conversion.
<Setter Property="Text" Value="{Binding Source={x:Static sys:DateTime.Now}, StringFormat='{}{0:MM/dd/yyyy hh:mm:ss}', Mode=OneWay}" />
On your second point where the value only shown when the button is pressed and goes away after button click is released, this is the expected behaviour. The Trigger Setter only apply when the binding condition is true, it will be revert to default value when the condition no longer true.
Please refer to MSDN site on the remark section which spells out above behaviour: http://msdn.microsoft.com/en-us/library/system.windows.trigger.aspx
Unless you have a very strong reason to stick with XAML based solution, code behind seems to be the better option here instead of tweaking with triggers.
Related
I attempted to follow a pattern described in an answer to this question.
How to set focus to textbox using MVVM?
However, I am having trouble with the concept of keyboard focus. If I have notepad or some other application running at the same time as my WPF application and click on notepad to put the keyboard focus there, then do something to cause my other application to put the focus into one of its text boxes, then the trigger gives the visual cue that my application's text box now has the keyboard focus. However when I start typing I can see that is not the case because the text is actually going into notepad.
Here is the xaml for my trigger.
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<DataTrigger Binding="{Binding ReadyForDataEntry}" Value="True">
<Setter Property="FocusManager.FocusedElement" Value="{Binding RelativeSource={RelativeSource Self}}" />
</DataTrigger>
<Trigger Property="IsKeyboardFocused" Value="true">
<Setter Property="Background" Value="Lavender"/>
<Setter Property="BorderBrush" Value="Blue"/>
</Trigger>
</Style.Triggers>
</Style>
Essentially the textbox will sometimes light up with the border and background color indicating that IsKeyboardFocused = true for that textbox, even though keyboard entry will be received by whatever application (e.g., one note, notepad) was last clicked in. What am I missing? Why would that WPF control have IsKeyboardFocused set true when the keyboard focus is clearly not true at all?
You're not doing anything wrong; this is a known quirk of WPF.
When a control receives logical focus, WPF attempts to give it keyboard focus as well. However, when you assign keyboard focus in an inactive WPF application, the application behaves as if it's active. That means, among other things, a focused TextBox will show a blinking caret, and IsKeyboardFocused and related properties will be set.
I have seen this issue in the past, and it was trivial to reproduce.
Xaml:
<Window x:Class="WpfTest.FocusTest"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<StackPanel HorizontalAlignment="Center"
VerticalAlignment="Center">
<TextBox x:Name="_textBox"
Width="150">
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="IsKeyboardFocused"
Value="true">
<Setter Property="Background"
Value="Lavender" />
<Setter Property="BorderBrush"
Value="Blue" />
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
<Button Margin="0,7,0,0"
Content="_Click Me"
Click="OnButtonClick" />
</StackPanel>
</Grid>
</Window>
Code behind:
public partial class FocusTest
{
public FocusTest()
{
InitializeComponent();
}
private void OnButtonClick(object sender, RoutedEventArgs e)
{
_textBox.Text = "";
// NOTE: Requires System.Reactive.Core, available in NuGet.
System.Reactive.Concurrency.Scheduler.Default.Schedule(
TimeSpan.FromSeconds(5),
() => this.Dispatcher.BeginInvoke(new Action(this.SetFocus)));
}
private void SetFocus()
{
_textBox.Text = "Focused";
FocusManager.SetFocusedElement(_textBox, _textBox);
}
}
Hit the button, Alt+Tab over to Notepad, and wait 5 seconds for the TextBox to receive focus. The IsKeyboardFocused trigger is fired, and the blinking caret appears, but keyboard input is still sent to Notepad.
The key point here is that the problem only arises when an element is given focus while another application is active (hence the artificial delay). Note that the problem still arises if you replace the SetFocusedElement call with Keyboard.Focus(_textBox), _textBox.Focus(), and other variations.
Unfortunately, I am not aware of a reliable, non-hacky way of fixing this issue. I don't recall how much time I spent on it, but I ultimately decided it wasn't worth the trouble. It's just not something that comes up that often.
I have a trouble with Telerik control.
<Style x:Key="RadDropDownButtonStyle" TargetType="telerik:RadDropDownButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="telerik:RadDropDownButton">
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="Red" />...
So, this handles Disabled property. Text changes its color, but it is not contrast, something like watermark.
How can I disable this? And make disabled control more contrast?
Text changes its color, but it is not contrast, something like watermark.
For future references, this is caused by the Opacity property. When the opacity is lower than 1 it becomes lighter.
The problem you are describing is something very familiar. And I'm afraid there isn't any great way to get rid of this, so much so that the best advise that I can give you is to not even bother. If anything, it will just make using the themes and styles a real pain.
However there is an easy way to get your desired behaviour by replacing the IsEnabled="False" property with IsHitTestVisible="False" and Focusable="False"
These will make it impossible to click or focus the control by keyboard navigation, basically making it disabled. Now you can add some more style properties to make it look the way you believe a disabled control should look like. By for example setting the wanted Foreground and Background or you could even use an Opacity closer to 1 (ex: 0.9) which will make it less dark than the original but still dark enough to read properly.
I hope this helps you with your current problem, just leave a comment if you want me to clarify further.
EDIT 1: Apperently can overwrite the opacity change by using your own DataTemplate for the control. How to have 100% opacity even when control is disabled in wpf C#
EDIT 2: I'll give you an example of how to use the other properties correctly.
This is how you would define a disabled button normally, and doing so will make the text lighter and less readable.
<!-- Simple disabled button -->
<telerik:RadButton Content="Test Button 1" IsEnabled="False" />
<!-- Button with binding on IsEnabled -->
<telerik:RadButton Content="Test Button 2" IsEnabled="{Binding MyBinding}" />
Now I'll show you how you can mimic these results by using the properties IsHitTestVisible and Focusable.
<!-- Simple disabled button -->
<telerik:RadButton Content="Test Button 1" IsHitTestVisible="False" Focusable="False" />
<!-- Button with binding on IsEnabled -->
<telerik:RadButton Content="Test Button 2" IsHitTestVisible="{Binding MyBinding}" Focusable="{Binding MyBinding}" />
In the above examples, the buttons will look as if they are still enabled. However you won't be able to focus or click them. Of course we do want to see some difference to be able to tell that they can't be used.
<!-- Styled disabled button -->
<telerik:RadButton Content="Test Button 1" IsHitTestVisible="False" Focusable="False" >
<telerik:RadButton.Style>
<Style TargetType="telerik:RadButton">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=Self}, Path=IsHitTestVisible}" Value="False">
<Setter Property="Opacity" Value="0.8"/>
</DataTrigger>
</Style.Triggers>
</Style>
</telerik:RadButton.Style>
</telerik:RadButton>
I've got a ComboBox I'm hiding and showing with a Style.Setter on the Visibility property:
<ComboBox ItemsSource="{Binding ElementName=BVTWindow, Path=DataContext.AreaList}" SelectedItem="{Binding Path=Area}">
<ComboBox.Style>
<Style TargetType="{x:Type ComboBox}">
<Style.Setters>
<Setter Property="Visibility" Value="Collapsed" />
</Style.Setters>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=BVTWindow, Path=DataContext.IdentitySelection}" Value="Test Management">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.Style>
</ComboBox>
This works great. Unfortunately, applying the style setter (for some reason) removes the color theme from the ComboBox. Now it's a light grey box with white text and nearly unreadable.
I tried adding Foreground and Background attributes to the ComboBox tag, but it had no effect.
I also tried adding Setter Properties for Foreground and Background to the DataTrigger, also with no effect.
I need to either stop the theme from being removed from the box, manually set the text color, or manually set the background color.
I read one article saying that Windows 8 (which is what I'm using) interferes with ComboBox styling, but there wasn't an easily-understandable solution for it.
Any advice would help me out a lot. Thanks in advance.
This articles explains it pretty well:
http://social.technet.microsoft.com/wiki/contents/articles/24240.changing-the-background-color-of-a-combobox-in-wpf-on-windows-8.aspx
Basically, Windows 8 has a different setup for ComboBox, and it breaks some of the style attributes. You can still style it, but you've got to right click the ComboBox and pick "edit template". This will generate a massive amount of xaml in your project view (color schemes for every possible combination of states), but you probably don't need all of it. I played with commenting / uncommenting sections to figure out what each Trigger and Setter affected, then set my colors and killed the extra parts.
I have a new solution that's much cleaner, faster, easier and better-looking than my previous idea:
Just wrap the ComboBox in a WrapPanel or StackPanel and apply the Style Setter to the parent. The control will retain it's proper style and the parent panel should be transparent anyway, so styles won't matter there. Worst case, you've got a WrapPanel with only one item in it and two extra lines of code in the xaml. This will work with other controls as well, as I just had to do it with a Checkbox.
Here's an example:
<WrapPanel>
<WrapPanel.Style>
<Style TargetType="{x:Type WrapPanel}">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding DataContext.TesterIdentitySelection.CanEdit, ElementName=BVTWindow}" Value="true">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</WrapPanel.Style>
<ComboBox ItemsSource="{Binding Path=DataContext.AreaList, ElementName=BVTWindow}" DisplayMemberPath="Name" SelectedItem="{Binding Path=Area, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" ToolTip="Select the testing area."/>
</WrapPanel>
The combo box is inside the wrap panel, and the wrap panel is what has the show/hide behavior applied to it. Since the wrap panel doesn't really have any style itself (just an invisible holder), everything ends up looking great.
Currently, I have the following datatrigger:
<Style TargetType="Button">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=ScheduleDataGrid, Path=HasItems}"
Value="false">
<Setter Property="Button.IsEnabled" Value="false"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
and I have two buttons that adds/deletes a row in a datagrid
<Button Name="BtnAddPoint" Content="Add" Width="70" Margin="10 0 10 0" Click="BtnAddSchedule_Click"></Button>
<Button Name="BtnDeletePoint" Content="Delete" Width="70" Click="BtnDeleteSchedule_Click"></Button>
I have two questions.
Currently, the above trigger disables both of the button when I only want it to disable the delete button. Setting the targetname of the setter to the delete button doesn't work. Can I make the trigger target a particular button?
Also, I'd like the delete button to be only enabled when a grid item is selected rather than checking for the item count. Is this possible?
You should use Style with key if you want to assign it to appropriate button:
<Style x:Key="DeleteButtonStyle" TargetType="{x:Type Button}">
<Setter Property="Button.IsEnabled" Value="True" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=ScheduleDataGrid, Path=SelectedItem}" Value="{x:Null}">
<Setter Property="Button.IsEnabled" Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
In BtnDeletePoint button you should add style:
<Button Name="BtnAddPoint" Content="Add" Width="70" Margin="10 0 10 0" Click="BtnAddSchedule_Click"></Button>
<Button Name="BtnDeletePoint" Content="Delete" Width="70" Click="BtnDeleteSchedule_Click" Style="{StaticResource DeleteButtonStyle}"></Button>
Instead of checking HasItems and writing another trigger to check if grid has selected item, you can write trigger and check if SelectedItem is null. SelectedItem property gives you information if grid has items and if user selected one of them.
If both Buttons share the same Style and therefore have the same Trigger, then they are both going to be disabled under the same circumstances (in this case, when the DataGrid has 0 items).
In order to disable the Delete Button under different circumstances you will need to create a separate Style with a different Trigger and apply that style to BtnDeletePoint. Since I don't see you setting the Style in the declaration of the buttons, I would guess the trigger belongs to an Implicit Style for Button, so you'll need to assign an x:Key to the new style so that you can assign it to your delete button:
<Style x:Key="DeleteButtonStyle" TargetType="Button">
<!-- Setters -->
<!-- Triggers -->
</Style>
<Button Name="BtnDeletePoint" Style="{DynamicResource DeleteButtonStyle}" Content="Delete" Width="70" Click="BtnDeleteSchedule_Click"/>
As for the trigger to enable the delete button when only a single item is selected, if you don't want to use the Count of the SelectedItems property on the DataGrid, then you would need to use a Converter to determine the selection state of the grid - there are no other DependencyProperties on the DataGrid that I'm aware of that will provide you that information.
Also, I'm not sure of the context without more of the code, but don't believe you want a DataTrigger in this case - you should be fine with a standard Trigger.
I have this XAML code:
<Button x:Name="ButtonUpdate" IsEnabled="False">
<Image Source="Images/update gray.png" Name="ImgUpdate" >
<Image.Style>
<Style TargetType="{x:Type Image}">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="True">
<Setter Property="Source" Value="Images/update.png" />
</Trigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
</Button>
I want that when ButtonUpdate is enabled:
ButtonUpdate.IsEnabled = true;
the ImgUpdate.Source become "Images/update.png", and when ButtonUpdate is not enabled:
ButtonUpdate.IsEnabled = false;
the ImgUpdate.Source become "Images/update gray.png" using data binding.
My XAML code doesn't work. Where is the error? How can I fix it?
Your Trigger applies to the Image, not the button, so the IsEnabled property that is monitored in this case is the Image.IsEnabled property, which is not what you want.
You can either create a Style for the Button and use a Trigger in that Style to change the Content of the button based on the IsEnabled state, or you could create a ControlTemplate with the Image as the content of the Button and change the Source of the Image based on the enabled state of the button.
This Answer seems to provide the specifics for what you're asking.
The image source property should be in this format:
"/«YourAssemblyName»;component/«YourPath»/«YourImage.png»"
Example
<Image Source="/WPFApplication;component/Images/Start.png" />
See this post for more info:
WPF image resources