When I check or uncheck a CheckBox, I want to clear the TextBox and set focus. I implemented it using code-behind.
My xmal:
<CheckBox x:Name="checkbox1" Checked="checkbox1_Checked" Unchecked="checkbox1_Checked" />
<TextBox x:Name="textbox1" Text="Hello" />
My C#:
private void checkbox1_Checked(object sender, RoutedEventArgs e)
{
textbox1.Text = "";
textbox1.Focus();
}
Is it possible to do it all in XAML?
Use data triggers. They work by creating a binding to a regular property, which is then
monitored for changes.
For instance, consider the following example:
<Window x:Class="WpfTutorialSamples.Styles.StyleDataTriggerSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="StyleDataTriggerSample" Height="200" Width="200">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<CheckBox Name="cbSample" Content="Hello, world?" />
<TextBlock HorizontalAlignment="Center" Margin="0,20,0,0" FontSize="48">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Text" Value="No" />
<Setter Property="Foreground" Value="Red" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=cbSample, Path=IsChecked}" Value="True">
<Setter Property="Text" Value="Yes!" />
<Setter Property="Foreground" Value="Green" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</StackPanel>
</Window>
In this example, we have a CheckBox and a TextBlock. Using a DataTrigger, we bind the TextBlock
to the IsChecked property of the CheckBox. We then supply a default style, where the text
is "No" and the foreground colour is red, and then, using a DataTrigger, we supply a style for
when the IsChecked property of the CheckBox is changed to True, in which case we make it
green with a text saying "Yes!" (as seen on the screen shot).
you can find more in here.
Related
I need a text box with a button in it,it must display a default value but should still allow the user to type into a text that i need to store in my ViewModel property.
The button should reset the value to the default one.
I got few issues with this implementation:
When the user type into the textbox i would expect the bound property in my viewModel to update accordingly, but seems there is no binding anymore. (Binding is set two way)
(the binding and the DataContext is correct, as on load is displaying the value set from the ViewModel)
Once i type into the box and hit the revert button the text is assign to the property as expected, but the text box still display he same value type by the user.
Each time i move across tabs o click another control, the button responsible for revert the text back, needs to be clicked twice (looks like a focus issue) as once the focus is in the text box all is working normally.
I have created a Generic.xaml were i have defined the control template.
<Style x:Key="{x:Type local:RememberValue}" TargetType="{x:Type local:RememberValue}">
<Setter Property="Background" Value="{StaticResource RemeberValue_Background}" />
<Setter Property="BorderBrush" Value="{StaticResource RemeberValue_Border}" />
<Setter Property="Foreground" Value="{StaticResource RemeberValue_Foreground}" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="Focusable" Value="True" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:RememberValue}">
<Grid x:Name="LayoutGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<baseControlUi:IconButton
Grid.Column="0"
Height="22"
Grid.ZIndex="1"
Margin="0"
EllipseDiameter="19"
Focusable="True"
Visibility="{Binding ElementName=RememberValueControl, Path=IsDifferentValue, Converter={StaticResource BooleanToVisibilityConverter}}"
ButtonCommand="{TemplateBinding RevertCommand}"
ButtonIcon="{StaticResource RevertIcon}" />
<TextBox
Grid.ZIndex="0"
Foreground="{StaticResource RemeberValue_Foreground}"
Text="{TemplateBinding DisplayText}"
HorizontalAlignment="Stretch"
VerticalAlignment="Center" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
This is the usage in the View.
<StackPanel Width="400">
<remebervalue:RememberValue
DisplayText="{Binding DisplayText, UpdateSourceTrigger=PropertyChanged}"
DefaultValue="{Binding DefaultText, UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="Left" Width="400" />
</StackPanel>
the code behind of RemeberValue.cs ha DP registered for the DisplayText and the DefaultText
public static readonly DependencyProperty DisplayTextProperty =
DependencyProperty.Register(nameof(DisplayText), typeof(string), typeof(RememberValue), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnDisplayText_Changed));
public RememberValue()
{
RevertCommand = new SimpleCommand(Revert);
}
private void Revert()
{
DisplayText = DefaultValue;
}
public string DisplayText
{
get => (string)GetValue(DisplayTextProperty);
set => SetValue(DisplayTextProperty, value);
}
private static void OnDisplayText_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
RememberValue RememberValue = d as RememberValue;
}
Partial answer
First point: I believe you are mistaken in writing "Binding is set two way", as you are using TemplateBinding, which is always one-way. You should replace it with
Binding DisplayText, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay
Second point: fixed by the above
Third point: different issue, needs to be addressed in a different question.
I have a UserControl that is a ListView with a Canvas as the ItemsPanel. The ItemsSource of the ListView is bound to a CollectionViewSource with it's Source property bound to an ObservableCollection< DeviceItem >. DeviceItem is a custom class that, among other things, holds the X/Y coordinates of the ListViewItem/DeviceItem which gets serialized when the user wants to save their work. On PreviewMouseDown and PreviewTouchDown I check the ListViewItem that's being clicked/touched to make sure that the content of the ListViewItem is a DeviceItem so that as the user moved it around the screen the software can updates the coordinates on the DeviceItem. This works 99% of the time but I run into a problem (sometimes) after the user removes items from the collection and then adds new items. After removing/adding, when the user clicks/touches the ListViewItem it's Content is typeof MS.Internal.NamedObject and in that case I can't act on the item. I'm sure it's worth mentioning that the user is adding/removing from a separate control in another window that is bound to the same collection in our data model (ObservableCollection< DeviceItem >).
Currently the user has to close and reopen the software when this happens. I'm trying to find a way to recover without restarting the software.
<UserControl.Resources>
<Style x:Key="CanvasStyle" TargetType="{x:Type Canvas}">
<EventSetter Event="Loaded" Handler="Canvas_Loaded"/>
</Style>
<CollectionViewSource Source="{Binding DiagramCollection}" x:Key="radioPacksDataView" />
</UserControl.Resources>
<Grid>
<ListView Name="listBoxDevices"
Background="Transparent"
BorderThickness="0"
Stylus.IsPressAndHoldEnabled ="False"
ItemsSource="{Binding Source={StaticResource radioPacksDataView}}"
PreviewMouseMove="ListBox_PreviewMouseMove"
PreviewMouseDown="ListBox_PreviewMouseDown"
PreviewMouseUp="ListBox_PreviewMouseUp"
PreviewTouchDown="ListBox_PreviewTouchDown"
PreviewTouchMove="ListBox_PreviewTouchMove"
PreviewTouchUp="ListBox_PreviewTouchUp"
SnapsToDevicePixels="True"
IsHitTestVisible="{Binding AccessRights, ConverterParameter=Unlocked, Converter={StaticResource enumBooleanConverter}, Source={x:Static appData:Globals.CurrentUser}}"
VirtualizingPanel.IsVirtualizing="False"
IsSynchronizedWithCurrentItem="False">
<ListView.Style>
<Style>
<Setter Property="ItemsControl.ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<Canvas Style="{DynamicResource CanvasStyle}"/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=window, Path=IconStyle}" Value="0">
<Setter Property="ListView.ItemContainerStyle" Value="{DynamicResource MultiDeviceItemContainerStyle}" />
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=window, Path=IconStyle}" Value="1">
<Setter Property="ListView.ItemContainerStyle" Value="{DynamicResource MultiDeviceItemContainerStyleV2}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ListView.Style>
<ListView.LayoutTransform>
<ScaleTransform CenterX="0"
CenterY="0"
ScaleX="{Binding ZoomScale}"
ScaleY="{Binding ZoomScale}"/>
</ListView.LayoutTransform>
</ListView>
</Grid>
And then in code behind
private void ListBox_PreviewMouseDown(object sender, MouseEventArgs e)
{
if (m_selectedListViewItem == null)
{
m_selectedListViewItem = ItemsControl.ContainerFromElement((ListBox)sender, e.OriginalSource as DependencyObject) as ListViewItem;
//this is the section that fails after removing/adding items, object is typeof(MS.Internal.NamedObject)
if (m_selectedListViewItem.Content.GetType().IsSubclassOf(typeof(DeviceItem)))
{
m_selectedDeviceItem = m_selectedListViewItem.Content as DeviceItem;
}
}
}
I need to change the layout of my window based on what the user selects in a combo box. I've made a stab at what one way might be but feel like it is clunky and hacked together. Im certain their must be a cleaner MVVM solution.
My thoughts where to have multiple dock panels in my GroupBox Whose visibility is set to collapse. When the selection is made, the appropriate dockpanel will be set to visible. I attempted to find a way to do this inside the view model with no success. I also couldn't help but think my attempts are violating MVVM.
XAML
<GroupBox Header="Options">
<Grid>
<DockPanel LastChildFill="False" x:Name="syncWellHeadersDockPanel" Visibility="Collapsed">
<Button DockPanel.Dock="Right" Content="Test"></Button>
</DockPanel>
<DockPanel LastChildFill="False" x:Name="SyncDirectionalSurveyDockPanel" Visibility="Collapsed">
<Button DockPanel.Dock="Left" Content="Test02"></Button>
</DockPanel>
</Grid>
</GroupBox>
ViewModel - Property for Selected Item for ComboBox
private StoredActionsModel _selectedStoredAction = DefaultStoredAction.ToList<StoredActionsModel>()[0];
public StoredActionsModel SelectedStoredAction
{
get { return _selectedStoredAction; }
set
{
if (value != _selectedStoredAction)
{
// Unset Selected on old value, if there was one
if (_selectedStoredAction != null)
{
_selectedStoredAction.Selected = false;
}
_selectedStoredAction = value;
// Set Selected on new value, if there is one
if (_selectedStoredAction != null)
{
_selectedStoredAction.Selected = true;
}
OnPropertyChanged("SelectedStoredAction");
if (_selectedStoredAction.StoredActionID == 4)
{
//X:SyncWellHeaderDockPanel.visibility = true?????
}
}
}
}
Here's a pure-XAML way to do exactly what you're asking how to do. It's a bit verbose.
Notice that we no longer set Visibility in attributes on the DockPanels. If we still did that, the values set in the Style trigger would be overridden by the attributes. That's the way dependency properties work.
<GroupBox Header="Options">
<Grid>
<DockPanel LastChildFill="False" x:Name="syncWellHeadersDockPanel" >
<Button DockPanel.Dock="Right" Content="Test"></Button>
<DockPanel.Style>
<Style TargetType="DockPanel" >
<Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers>
<DataTrigger
Binding="{Binding SelectedStoredAction.StoredActionID}"
Value="1"
>
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</DockPanel.Style>
</DockPanel>
<DockPanel LastChildFill="False" x:Name="SyncDirectionalSurveyDockPanel">
<Button DockPanel.Dock="Left" Content="Test02"></Button>
<DockPanel.Style>
<Style TargetType="DockPanel" >
<Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers>
<DataTrigger
Binding="{Binding SelectedStoredAction.StoredActionID}"
Value="2"
>
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</DockPanel.Style>
</DockPanel>
</Grid>
</GroupBox>
Another way to do this would be to pass SelectedStoredAction.StoredActionID to a DataTemplateSelector, but that involves writing C# code that knows what your XAML resource keys are, and I'm not a fan.
I want to have a StackPanel who's visibility should be depending on a Combobox selection. Unfortunatly the XAML below does not work.
I found a solution with a new property which will be set on the PropertyChanged event of the Combobox selection, though I would prefer a strict XAML solution for this.
Any hints on how to solve this?
<StackPanel>
<Label>Picture in Picture function</Label>
<ComboBox Name="cbPictureInPicture" ItemsSource="{Binding Path=PictureInPictureCodeList, Mode=OneWay}" DisplayMemberPath="CodeText"
SelectedValuePath="CodeID" SelectedValue="{Binding Path=PictureInPicture, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
<StackPanel>
<StackPanel.Style>
<Style TargetType="StackPanel">
<Setter Property="Visibility" Value="Visible" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=cbPictureInPicture, Path=IsSelected.CodeText}" Value="Yes">
<Setter Property="Visibility" Value="Hidden"/>
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
<Label>Picture in Picture is used</Label>
(...)
</StackPanel>
you may perhaps rewrite the same as
<DataTrigger Binding="{Binding ElementName=cbPictureInPicture, Path=SelectedItem.CodeText}" Value="Yes">
<Setter Property="Visibility" Value="Hidden"/>
</DataTrigger>
assuming the combobox is bound to a collection whose item has CodeText property. so SelectedItem.CodeText will point to the same.
additionally it may not be required to set <Setter Property="Visibility" Value="Visible" /> as it is the default value. it does not have any effect in this case just some extra line of code which can be removed.
You can also use a converter and bind directly to the PictureInPicture property:
<StackPanel Visibility="{Binding PictureInPicture, Converter={StaticResource myVisibilityConverter}}"/>
<Label>Picture in Picture is used</Label>
(...)
</StackPanel>
Create flags and pass this flag in stackpanel visibility converter.
On the basis of flag in converter make decision stackpanel visible/hide whatever
Set this flat in comboBox selection change event if selected value as per your requirement.
I am using the DevComponents TabNavigation control for WPF, and am able to add a new TabItem to the TabNavigation at a specific index, call it i, in the code-behind. Now I want to make the new TabItem the SelectedItem, by doing:
private void textBlock_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
int i = createNewTabItem(0, "Foo");
TabNavigation tn = (((sender as TextBlock).Parent as Grid).Parent as TabItem).Parent as TabNavigation;
tn.SelectedItem = tn.Items[i];
}
private int createNewTabItem(int overflowSrcPageNum, String header)
{
TabItem ti = new TabItem();
ti.Header = header;
tabNavigation.Items.Insert(overflowSrcPageNum + 1, ti);
return overflowSrcPageNum + 1;
}
When I run this code, however, instead of the new TabItem being brought into view, it is brought into view and then the original tab I was on is quickly moved back into view.
If anyone has any ideas as to why this is happening, and how I can fix it please let me know. I have attached a sample of the XAML below:
<Grid >
<Grid.Resources>
<ResourceDictionary>
<Style TargetType="TextBlock">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="TextDecorations" Value="Underline"></Setter>
</Trigger>
</Style.Triggers>
<Setter Property="Foreground" Value="White" />
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontSize" Value="11" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="HorizontalAlignment" Value="Right" />
<Setter Property="Text" Value="View More..." />
<Setter Property="Visibility" Value="Visible" />
<EventSetter Event="MouseLeftButtonDown" Handler="lblMoreCpartys_MouseLeftButtonDown" />
</Style>
</ResourceDictionary>
</Grid.Resources>
<my:TabNavigation Background="Black" HorizontalAlignment="Stretch" Margin="0" Name="tabNavigation"
VerticalAlignment="Stretch" MouseLeftButtonDown="tabNavigation_MouseLeftButtonDown"
FontSize="12" Foreground="SteelBlue" ForceCursor="True" MouseWheel="tabNavigation_MouseWheel"
TabStripPlacement="Bottom">
<TabItem Header="ITEM 1" Name="firstTabItem" FontSize="12" >
<TextBlock Name="firstTB" />
</TabItem>
<TabItem Header="ITEM 2" Name="secondTabItem" FontSize="12" >
<TextBlock Name="secondTB" />
</TabItem>
</my:TabNavigation>
</grid>
Thanks in advance.
Try setting e.Handled to True in textBlock_MouseLeftButtonDown.
I'm not familiar with that control, but if it works like TabControl then it has logic to bring a tab into view when it is clicked. That logic sees that the original tab was clicked, and brings it back into view after your change. Marking the EventArgs object as Handled will stop WPF from calling event handlers on parent elements, which would stop the tab from switching back.