How i can bind StaticResource key in View model - c#

I have different ControlTemplates for one Canvas:
<Application.Resources>
<ControlTemplate x:Key="Control1" />
<ControlTemplate x:Key="Control2" />
</Application.Resources>
I want to change one of them by my viewmodel property like this:
private string _template = "Control1";
public string Template
{
get
{
return _template;
}
set
{
if (!string.IsNullOrEmpty(value))
{
_template = value;
OnPropertyChanged("Template");
}
}
}
And finally use it in my view:
<UserControl Template="{StaticResource {Binding Template}}" />
But it doesn't work, how i can fix it?
Thanks

You can try using DataTriggers
<UserControl>
<UserControl.Style>
<Style TargetType="{x:Type UserControl}">
<Setter Property="ControlTemplate" value="{StaticResource Control1}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Template}" Value ="Control1">
<Setter Property="ControlTemplate" value="{StaticResource Control1}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Template}" Value ="Control2">
<Setter Property="ControlTemplate" value="{StaticResource Control2}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</UserControl.Style>
</UserControl>

Related

Image Visibility DataTrigger Set Default to False

I have an image that need to be showed based on condition, is that attachment file or not. The problem is, I've set trigger that set the value of the condition, but seems like the condition isn't work and the value always set to true.
<Image
Width="30"
Height="30"
Source="Resources/Images/chat_file_attach.png">
<Image.Style>
<Style TargetType="{x:Type Image}">
<Style.Triggers>
<DataTrigger Binding="{Binding AttachStat}" Value="False">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
<DataTrigger Binding="{Binding AttachStat}" Value="True">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
The question is. Is there any way to make the default value to false? I've set it to true on the C# looping data, whenever the condition is attachment included.
Take a look in your output window and search for:
System.Windows.Data Warning: 40 : BindingExpression path error
I think the AttachStat property is not available in the image's DataContext.
Use a single DataTrigger and make sure that the DataContext of the Image has a public AttachStat property:
<Image x:Name="img" Width="30" Height="30" Source="Resources/Images/chat_file_attach.png">
<Image.Style>
<Style TargetType="{x:Type Image}">
<Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers>
<DataTrigger Binding="{Binding AttachStat}" Value="True">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
img.DataContext = new YourClass();
...
public class YourClass : INotifyPropertyChanged
{
private bool _attachStat;
public bool AttachStat
{
get
{
return _attachStat;
}
set
{
_attachStat = value;
NotifyPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
You can use a BoolToVisibilityConverter to convert a Boolean to and from a Visibility value.
In the resource section:
<BooleanToVisibilityConverter x:Key="BoolToVisibilityConverter" />
And the image:
<Image Width="30"
Height="30"
Source="Resources/Images/chat_file_attach.png"
Visibility="{Binding AttachStat,
Converter={StaticResource BoolToVisibilityConverter}}" />

WPF MVVM Interaction Binding CommandParameter to UI element

I want the text in a TextBox to get selected when the TextBox gets focused.
Therefore I need to binding a Command to the "GotFocus" event. The special thing is, that the TextBox is created dynamically via an ItemsControl.
So there is a binding to the UserControl (View), the ItemsControl and the Item itself. When I tried to bind the UI element to the CommandParameter I just got the Model bindet to the current item in the ItemsControl.
All the bindings are working perfectly except the CommandParameter..
Somebody got an idea how to get this working?
Here is my code:
XAML
/////// <UserControl/> Information:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
x:Name="MainBindingControl"
///////
<ItemsControl ItemsSource="{Binding MySecondModelList }" Margin="10,10,10,0">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid helper:GridHelper.RowCount="{Binding MyFirstModel.Rows}" helper:GridHelper.ColumnCount="{Binding MyFirstModel.Columns}">
</Grid>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Grid.Row" Value="{Binding Row}" />
<Setter Property="Grid.Column" Value="{Binding Column}" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Margin="25,25,25,25" Height="30" Width="30" Text="{Binding Text, Mode=TwoWay, UpdateSourceTrigger=LostFocus}" TextAlignment="Center" VerticalContentAlignment="Center" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="GotFocus">
<i:InvokeCommandAction Command="{Binding ElementName=MainBindingControl, Path=DataContext.TextBoxFocusCommand}" CommandParameter="{Binding RelativeSource={ RelativeSource Self }}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<TextBox.Style>
<Style TargetType="TextBox">
<Setter Property="Background" Value="OrangeRed" />
<Style.Triggers>
<Trigger Property="Text" Value="0">
<Setter Property="Background" Value="Orange" />
</Trigger>
<Trigger Property="Text" Value="1">
<Setter Property="Background" Value="White" />
</Trigger>
<Trigger Property="Text" Value="2">
<Setter Property="Background" Value="White" />
</Trigger>
<Trigger Property="Text" Value="3">
<Setter Property="Background" Value="White" />
</Trigger>
<Trigger Property="Text" Value="4">
<Setter Property="Background" Value="White" />
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
CS
#region TextBoxFocus
private ICommand _textBoxFocusCommand;
public ICommand TextBoxFocusCommand
{
get { return _textBoxFocusCommand; }
set { _textBoxFocusCommand = value; }
}
public void TextBoxFocus(object parameter)
{
var _tmp = parameter as TextBox;
if (_tmp != null )
{
_tmp.SelectAll();
}
}
#endregion
Models
public class FirstModel
{
public int Rows { get; set; }
public int Columns { get; set; }
}
public class SecondModel
{
public int Row { get; set; }
public int Column { get; set; }
public string Text { get; set; }
}
public class ViewModel
{
public FirstModel MyFirstModel { get; set; }
public ObservableCollection<SecondModel> MySecondModelList { get; set; }
}
Since what you want to do is only related to the view, I'd just add the code in the code-behind, instead of trying to use commands and get the TextBox inside the ViewModel. In MVVM you should NEVER reference UI assemblies from the ViewModel. But it is ok for you to use code-behind if what you are trying to do is only related to the View.
So, inside the style of the TextBox, you would have:
<EventSetter Event="GotFocus" Handler="TextBox_GotFocus"/>
And then in the code-behind of the UserControl:
private void TextBox_GotFocus(object sender, RoutedEventArgs e)
{
TextBox textBox = sender as TextBox;
textBox.SelectAll();
}
The complete code of your DataTemplate would then be:
<DataTemplate>
<TextBox Margin="25,25,25,25" Height="30" Width="30" Text="{Binding Text, Mode=TwoWay, UpdateSourceTrigger=LostFocus}" TextAlignment="Center" VerticalContentAlignment="Center" >
<!-- Just erase this block of code
<i:Interaction.Triggers>
<i:EventTrigger EventName="GotFocus">
<i:InvokeCommandAction Command="{Binding ElementName=MainBindingControl, Path=DataContext.TextBoxFocusCommand}" CommandParameter="{Binding RelativeSource={ RelativeSource Self }}"/>
</i:EventTrigger>
</i:Interaction.Triggers>-->
<TextBox.Style>
<Style TargetType="TextBox">
<EventSetter Event="GotFocus" Handler="TextBox_GotFocus"/>
<Setter Property="Background" Value="OrangeRed" />
<Style.Triggers>
<Trigger Property="Text" Value="0">
<Setter Property="Background" Value="Orange" />
</Trigger>
<Trigger Property="Text" Value="1">
<Setter Property="Background" Value="White" />
</Trigger>
<Trigger Property="Text" Value="2">
<Setter Property="Background" Value="White" />
</Trigger>
<Trigger Property="Text" Value="3">
<Setter Property="Background" Value="White" />
</Trigger>
<Trigger Property="Text" Value="4">
<Setter Property="Background" Value="White" />
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</DataTemplate>
Notice that the method SelectAll() of the TextBox called on GotFocus event has a little trick to work as intended. Check this SO question: How to automatically select all text on focus in WPF TextBox?

Creating a self-updating Textblock user control in WPF

I'm trying to create a re-usable textblock user control in WPF. The basic idea is as follows:
User does not directly specify the content of the textblock
There are three dependancy properties in my user control called IsToggled, ToggleTrueText, and ToggleFalseText.
The control will display ToggleTrueText if IsToggled is true; or display ToggleFalseText if IsToggled is false.
When IsToggled changes during runtime, the text automatically changes to either ToggleTrueText or ToggleFalseText
I started by adding a PropertyChangedCallback to the IsToggled DP:
Code-behind of the UserControl:
public static readonly DependencyProperty IsToggledProperty =
DependencyProperty.Register("IsToggled", typeof(bool),
typeof(TagToggle), new PropertyMetadata(new
PropertyChangedCallback(OnToggleStateChanged)));
public bool IsToggled
{
get { return (bool)GetValue(IsToggledProperty); }
set { SetValue(IsToggledProperty, value); }
}
//ToggleTrueText and ToggleFalseText are declared similarly to IsToggled
...
private static void OnToggleStateChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
...
}
Xaml of the user control:
<Grid x:Name="LayoutRoot">
<TextBlock x:Name="TheTextBlock" Text="{Binding WhatDoIBindTo}"/>
</Grid>
However, I'm not sure what would be the best way to ensure that TheTextBlock updates its text whenever IsToggled changes during runtime.
Try this:
private static void OnToggleStateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
TagToggle ctrl = d as TagToggle;
if (ctrl != null)
{
TheTextBlock.Text = ctrl.IsToggled ? ToggleTrueText. : ToggleFalseText;
}
}
If you want to bind the Text property of the TextBlock you need to make sure that you are binding to properties of the UserControl. You could do this by setting the DataContext property of the TextBlock:
<TextBlock x:Name="TheTextBlock" DataContext="{Binding RelativeSource={RelativeSource AncestorType=UserControl}}">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Text" Value="{Binding ToggleTrueText}" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsToggled}" Value="False">
<Setter Property="Text" Value="{Binding ToggleFalseText}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
You can used trigger for this
Please check below code
<TextBlock x:Name="TheTextBlock">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsToggled}" Value="True">
<Setter Property="Text" Value="{Binding ToggleTrueText}"/>
</DataTrigger>
<DataTrigger Binding="{Binding IsToggled}" Value="False">
<Setter Property="Text" Value="{Binding ToggleFalseText}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>

Highlight one item in DataGrid

I'm displaying Cars in a DataGrid and would like to highlight one special car, the CurrentlySelectedCar.
When the user double clicks on a car, this car is saved as CurrentlySelectedCar in my MainViewModel. Now, whenever the user comes back to the DataGrid, I would like to highlight this car = row, e.g. by using a red background.
I have found out how to highlight rows in a DataGrid based on certain values, but in my case, all I have is the CurrentlySelectedCar.
My First try:
<Style TargetType="DataGridRow">
<Style.Triggers>
<!-- not working-->
<DataTrigger Binding="{Binding CurrentlySelectedCar}" >
<Setter Property="Background" Value="Red"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
My second try:
<Style TargetType="DataGridRow">
<Style.Triggers>
<!-- not working either, "Binding can only be set on DependencyProperty of DependecyObject"-->
<DataTrigger Binding="{Binding Guid}" Value="{Binding CurrentlySelectedCar.Guid}" >
<Setter Property="Background" Value="Red"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
How can I highlight the row with this information?
I think that you have to do something like this described in this answer: Using bindings in DataTrigger condition
<Style TargetType="DataGridRow">
<Style.Triggers>
<DataTrigger Value="True">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource someMultiConverter}">
<Binding Path="Guid"></Binding>
<Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type Datagrid}}" Path="CurrentlySelectedCar.Guid"></Binding>
</MultiBinding>
</DataTrigger.Binding>
<Setter Property="Background" Value="Red"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
You have to write a multiconverter that return true if the two Guid are equals.
Try the following instead, since each datagrid row has an IsSelected property, you can bind to it directly.
<DataGrid EnableRowVirtualization="False">
<DataGrid.Resources>
<Style TargetType="DataGridRow">
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected}" >
<Setter Property="Background" Value="Red"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.Resources>
</DataGrid>
Updated response
The problem right now is your DataContext is the DataGridRow, and it doesn't have access to the MainViewModel. You can address this by passing the DataGridRow to a converter that is aware of the current MainViewModel. However, it is a lot of code, and is barely comprehensible.
<Window>
<Window.Resources>
<local:IsCurrentlySelectedCarConverter x:Key="IsCurrentlySelectedCarConverter" />
</Window.Resources>
...
<DataTrigger Binding="{Binding
Path=DataContext,
Converter={StaticResource IsCurrentlySelectedCarConverter}}" >
<Setter Property="Background" Value="Red" />
</DataTrigger>
</Window>
Here's corresponding converter
public class IsCurrentlySelectedConverter : IValueConverter
{
public MainViewModel MainViewModel { get; set;}
public object Convert(object value, ....)
{
return (value == MainViewModel.CurrentlySelectedCar);
}
}
and you'll need to wire in the converter's MainViewModel manually in your view
this.Resources["IsCurrentlySelectedCarConverter"].MainViewModel = _mainViewModel;
and at this point you'd have to question the maintainability of the monster that has been created.
It may be better to replace each Car with a CarViewModel so that it has a property called IsSelected and you can maintain that in code. The following in my opinion is easier to follow what is actually going on.
public class CarViewModel : INotifyPropertyChanged
{
public MainViewModel { get; set; }
public bool IsSelected { get { return this == MainViewModel.CurrentlySelectedCar; } }
// Call RaisePropertyChanged("IsSelected") whenever
// CurrentlySelectedCar is changed
public void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName);
}
}

Setting the content property dynamically using a custom property XAML WPF

I'm creating an onscreen keyboard for a touch screen app, where shift toggles upper and lower case buttons on the whole keyboard.
The code in the c# is working but I don't know how to change the content value and command parameter of the buttons based on on my custom property which changes on a bool value, in xaml.
<local:KeyboardButton Grid.Row="0" Grid.Column="2" Grid.ColumnSpan="2" Command="{Binding AddText}" Content ="{Binding local:KeyboardButton.SelectedKey}" LowerCaseKey="`" UpperCasekey="¬"/>
This is what I have currently for each button in the XAML (ignore the Content, as I've been grasping at straws here), the idea is that the shift key will toggle the Content and CommandParameter between the LowerCaseKey and UpperCaseKey properties.
maybe you could achieve your goal with styles and triggers:
<Button Grid.Row="0" Grid.Column="2" Grid.ColumnSpan="2" Command="{Binding AddText}" x:Name="AButton">
<Button.Resources>
<Style TargetType="Button">
<Setter Property="Content" Value="{Binding Path=LowerCaseKey, ElementName=AButton}" />
<Setter Property="CommandParameter" Value="{Binding Path=LowerCaseKey, ElementName=AButton}" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsUpperCase}" Value="true">
<Setter Property="Content" Value="{Binding Path=UpperCasekey, ElementName=AButton}" />
<Setter Property="CommandParameter" Value="{Binding Path=UpperCasekey, ElementName=AButton}" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Resources>
</Button>
Custom Control:
using System.Windows;
using System.Windows.Controls;
namespace Test
{
public class KeyboardButton : Button
{
public static readonly DependencyProperty SelectedKeyProperty = DependencyProperty.Register("SelectedKey", typeof(string),
typeof(KeyboardButton), new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.AffectsArrange));
public static readonly DependencyProperty IsUpperCaseProperty = DependencyProperty.Register("IsUpperCase", typeof(bool),
typeof(KeyboardButton), new FrameworkPropertyMetadata(false));
static KeyboardButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(KeyboardButton), new FrameworkPropertyMetadata(typeof(KeyboardButton)));
}
public string SelectedKey
{
get { return (string)GetValue(SelectedKeyProperty); }
set { SetValue(SelectedKeyProperty, value); }
}
public string LowerCaseKey
{
get;
set;
}
public string UpperCaseKey
{
get;
set;
}
public bool IsUpperCase
{
get { return (bool)GetValue(IsUpperCaseProperty); }
set { SetValue(IsUpperCaseProperty, value); }
}
}
}
Themes\Generic.xaml (file Generic.xaml in the Themes folder)
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Test">
<Style TargetType="{x:Type local:KeyboardButton}" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Content" Value="{Binding LowerCaseKey, Mode=OneTime, RelativeSource={RelativeSource Self}}"/>
<Style.Triggers>
<Trigger Property="IsUpperCase" Value="true">
<Setter Property="Content" Value="{Binding UpperCaseKey, Mode=OneTime, RelativeSource={RelativeSource Self}}"/>
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
Don't forget this in AssemblyInfo.cs:
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]

Categories

Resources