How could I bind a brush from my ViewModel to a setter value?
<DataGrid.CellStyle>
<Style TargetType="{x:Type DataGridCell}">
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="{Binding SelectionBrush, Mode=OneWay}" />
<Setter Property="BorderBrush" Value="{Binding SelectionBrush, Mode=OneWay}" />
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.CellStyle>
class Leerformular
{
public Leerformular()
{
SelectionBrush = (SolidColorBrush)(new BrushConverter()).ConvertFromString(settings.SelectionBrush);
}
public SolidColorBrush SelectionBrush
{
get { return selectionBrush; }
set
{
if (selectionBrush != value && value != null)
{
selectionBrush = value;
OnPropertyChanged("SelectionBrush");
}
}
}
}
When I execute the program nothing is happening when I select the cells.
Related
I'm creating Radio Buttons dynamically that means I'm looping throught my database items and I'm displaying styled radio buttons and here's my code:
public ObservableCollection<Product> products = new ObservableCollection<Product>(ProductsController.SelectAllProducts());
if (products.Count > 0)
{
foreach(var item in products)
{
SolidColorBrush mySolidColorBrush = new SolidColorBrush();
mySolidColorBrush = (SolidColorBrush)(new BrushConverter().ConvertFrom("#004a80"));
RadioButton a = new RadioButton();
a.BorderThickness = new Thickness(1);
a.Background = Brushes.Green;
a.Foreground = new SolidColorBrush(Colors.Black);
a.BorderBrush = mySolidColorBrush;
a.Width = 118;
a.Height = 70;
a.Margin = new Thickness(5,0,0,5);
Style style = Application.Current.Resources["MyRadioButtonAsButtonStyle"] as Style;
a.Style = style;
a.ApplyTemplate();
a.Content = item.OrdinalNumber;
Image imgControl = (Image)a.Template.FindName("img", a);
Image imgControlWhite = (Image)a.Template.FindName("whiteImg", a);
TextBlock text = (TextBlock)a.Template.FindName("text", a);
if (fileNames.Count > 0)
{
if (!fileNames.Any(item.Description.Contains))
{
item.IsProcessed = true; // SETTING PROPERTY
}
}
a.Click += (object sender, RoutedEventArgs e) =>
{
var radioButton = sender as RadioButton;
MessageBox.Show(radioButton.Content.ToString());
};
text.Text = item.Title;
imgControl.Source = image;
spProducts.Children.Add(a);
}
}
I'm setting IsProcessed which I would like to use when I'm setting background of this radio button which is styled as button.
So for example if IsProcessed = true I would like to set Background to Green, otherwise I would like to set it to Red.
Here is my class:
enter code herepublic class Product : INotifyPropertyChanged
{
#region Attributes
private string _ordinalNumber;
private string _title;
private string _description;
private bool _isProcessed;
#endregion
#region Properties
public string OrdinalNumber
{
get { return _ordinalNumber; }
set { _ordinalNumber = value; NotifyPropertyChanged("OrdinalNumber"); }
}
public string Title
{
get { return _title; }
set { _title = value; NotifyPropertyChanged("Title"); }
}
public string Description
{
get { return _description; }
set { _description = value; NotifyPropertyChanged("Description"); }
}
public bool IsProcessed
{
get { return _isProcessed; }
set
{
_isProcessed = value; NotifyPropertyChanged("IsProcessed");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
And here is my custom style where I've set background for first time (To Green):
<Style x:Key="MyRadioButtonAsButtonStyle" TargetType="RadioButton">
<Setter Property="FontSize" Value="15" />
<Setter Property="GroupName" Value="Option" />
<Setter Property="BorderThickness" Value="1.5" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<!-- and so on for each property...-->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type RadioButton}">
<Border Height="{Binding ActualHeight, ElementName=parentStackPanel}" Width="{Binding ActualWidth, ElementName=parentStackPanel}" BorderBrush="#004a80" BorderThickness="{TemplateBinding BorderThickness}">
<Grid x:Name="gridProduct" Background="Green">
<Grid.RowDefinitions>
<RowDefinition Height="80*"></RowDefinition>
<RowDefinition Height="20*"></RowDefinition>
</Grid.RowDefinitions>
<Image x:Name="slika" Margin="10" Grid.Row="0" Width="Auto" Visibility="Visible"/>
<TextBlock x:Name="tekst" Foreground="White" Margin="0,0,0,3" Grid.Row="1" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="12"></TextBlock>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="gridProduct" Property="Background" Value="Orange"/>
<Setter TargetName="tekst" Property="Foreground" Value="White"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
So basically I don't know how to check value of item.IsProcessed in app.xaml so I might somehow add setter for background depending on that prop value?
Any kind of help would be great!
Thanks!
How about adding a DataTriggerto your Style?:
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="gridProduct" Property="Background" Value="Orange"/>
<Setter TargetName="tekst" Property="Foreground" Value="White"/>
</Trigger>
<DataTrigger Binding="{Binding IsProcessed}" Value="True">
<Setter TargetName="gridProduct" Property="Background" Value="Green"/>
</DataTrigger>
</ControlTemplate.Triggers>
For the binding to work, you should set the DataContext of the RadioButton:
foreach (var item in products)
{
...
a.DataContext = item;
spProducts.Children.Add(a);
}
In general, you would use an ItemsControl that binds to the ObservableCollection<Product> and define the RadioButton in the ItemTemplate.
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?
I have implemented IDataErrorInfo to my custom Controls' ViewModels. Everything works fine (border is being drawn red, a tooltip with the error is being shawn), but i was wondering if there is a way to have two different Validation.ErrorTemplates for errors and warnings.
My Custom Control Style (with the Validation.ErrorTemplate)
<Style TargetType="{x:Type controls:CustomTextBoxNumeric}">
<Setter Property="TextAlignment" Value="Right"/>
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<DockPanel LastChildFill="True">
<Border BorderBrush="Red" BorderThickness="1">
<AdornedElementPlaceholder />
</Border>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={RelativeSource Self},
Path=(Validation.Errors).CurrentItem.ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
My Custom Control ViewModel (INotifyPropertyChanged is implemented at the base ViewModel)
public class CustomTextBoxNumericViewModel : BaseComponentViewModel, IDataErrorInfo
{
private decimal? decimalValue;
private bool hasErrors;
private bool hasWarnings;
public CustomTextBoxNumericViewModel()
{
}
[DataMember(EmitDefaultValue = false)]
public decimal? DecimalValue
{
get { return this.decimalValue; }
set { this.decimalValue = value; this.Changed("DecimalValue"); this.Changed("HasErrors"); }
}
[DataMember(EmitDefaultValue = false)]
public bool HasErrors
{
get { return this.hasErrors; }
set { this.hasErrors = value; this.Changed("HasErrors"); this.Changed("DecimalValue"); }
}
[DataMember(EmitDefaultValue = false)]
public bool HasWarnings
{
get { return this.hasWarnings; }
set { this.hasWarnings = value; this.Changed("HasWarnings"); this.Changed("DecimalValue"); }
}
#region IDataErrorInfo Implementation
public string Error
{
get
{
throw new NotImplementedException();
}
}
public string this[string propertyName]
{
get
{
if (propertyName == "DecimalValue")
{
if (HasErrors)
{
return this.ErrorsField;
}
if (HasWarnings)
{
return this.WarningsField;
}
if (DecimalValue < 0)
{
return "Must be greater than 0";
}
}
return string.Empty;
}
}
#endregion
}
I managed to solve my issue using ControlTemplate resources.
My style changed to:
<Style TargetType="{x:Type controls:CustomTextBoxNumeric}">
<Setter Property="TextAlignment" Value="Right"/>
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<DockPanel LastChildFill="True">
<Border BorderBrush="Red" BorderThickness="1">
<AdornedElementPlaceholder />
</Border>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={RelativeSource Self},
Path=(Validation.Errors).CurrentItem.ErrorContent}"/>
</Trigger>
<DataTrigger Binding="{Binding Path=ViewModel.HasWarnings, RelativeSource={RelativeSource Self}}" Value="True">
<Setter Property="Validation.ErrorTemplate" Value="{DynamicResource EntypoWarningTemplate}" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=ViewModel.HasErrors, RelativeSource={RelativeSource Self}}" Value="True">
<Setter Property="Validation.ErrorTemplate" Value="{DynamicResource EntypoErrorTemplate}" />
</DataTrigger>
</Style.Triggers>
</Style>
The ControlTemplates:
<ControlTemplate x:Key="MyErrorTemplate" TargetType="{x:Type Control}">
<DockPanel LastChildFill="True">
<Border BorderBrush="Red" BorderThickness="1">
<AdornedElementPlaceholder />
</Border>
</DockPanel>
</ControlTemplate>
<ControlTemplate x:Key="MyWarningTemplate" TargetType="{x:Type Control}">
<DockPanel LastChildFill="True">
<Border BorderBrush="Orange" BorderThickness="1">
<AdornedElementPlaceholder />
</Border>
</DockPanel>
</ControlTemplate>
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);
}
}
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>