I have a situation:
DataGrid with TemplateColumn. Template contains Stack panel with CheckBox and TextBox. What I want is when I press CheckBox then TextBox which is on the same StakcPanel set value to 1.
So in simple
CheckBox -> IsChecked==true -> Set TextBox value to 1 (TextBox on the same StackPanel as CheckBox)
I try with RelativeSource but not work.
Thanks For help.
Well you can bind the Content of the TextBox TextForTextBox, and bind the Checked state to Checked. in your viewModel you would : do
private string _text;
private bool _checked;
public string TextForTextBox
{
get { return _text; }
private set { _text = value;}
}
public bool Checked
{
get { return _checked; }
set
{
if (_checked != value)
{
_checked = value;
Text = _checked ? 1 : = 0;
PropertyChanged("Checked");
PropertyChanged("TextForTextBox");
}
}
}
This is assuming that you have a ViewModel that implements the INotfiyPropertyChanged.
EDIT
Code to change this in pure XAML :
<Grid>
<StackPanel>
<CheckBox x:Name="check" />
<TextBox>
<TextBox.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=check, Path=IsChecked}" Value="True">
<Setter Property="TextBox.Text" Value="1" />
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=check, Path=IsChecked}" Value="False">
<Setter Property="TextBox.Text" Value="0" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</StackPanel>
</Grid>
Related
I am new to WPF and all this magical binding and datatriggers stuff, so i ask you for a little help.
I have a simple wpf app shown on picture below.
I want my datagrid contents to reflect conditions and date filter. I already figured out how to bind datagrid rows visibility depending on event codes and checkboxes (start, stop, error). But i cant't figure out how to implement date filtering. All i want is: when "Filter by date" checkbox is checked, in my datagrid only those rows remain visible, which have date in "server time" field (i guess i need to parse it somehow from datetime) equal to selected date combobox.
Can i achieve that using xaml only? Can enyone help me to do that?
Here is xaml for my datagrid:
<DataGrid
Grid.Row="1"
Margin="5"
AutoGenerateColumns="False"
IsReadOnly="True"
ItemsSource="{Binding LogEntries}"
Style="{DynamicResource Helvetica}">
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Style.Triggers>
<DataTrigger Binding="{Binding event_code}" Value="1">
<Setter Property="Background" Value="LightGreen" />
<Setter Property="Visibility" Value="{Binding IsChecked, ElementName=StartShowChecked, Converter={StaticResource BooleanToVisibilityConverter}}" />
</DataTrigger>
<DataTrigger Binding="{Binding event_code}" Value="2">
<Setter Property="Background" Value="LightGray" />
<Setter Property="Visibility" Value="{Binding IsChecked, ElementName=StopShowChecked, Converter={StaticResource BooleanToVisibilityConverter}}" />
</DataTrigger>
<DataTrigger Binding="{Binding event_code}" Value="3">
<Setter Property="Background" Value="#FFEA816F" />
<Setter Property="Visibility" Value="{Binding IsChecked, ElementName=ErrorShowChecked, Converter={StaticResource BooleanToVisibilityConverter}}" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding server_datetime, StringFormat=\{0:dd.MM.yy HH:mm:ss\}}" Header="Server time" />
<DataGridTextColumn Binding="{Binding user_datetime, StringFormat=\{0:dd.MM.yy HH:mm:ss\}}" Header="Client time" />
<DataGridTextColumn
Width="*"
Binding="{Binding log_entry}"
Header="Entry" />
</DataGrid.Columns>
Can i achieve that using xaml only?
No you can't because XAML is a markup language and nothing else.
What you shold do is to bind the SelectedItem of the date ComboBox to a DateTime property of your view model and bind the IsChecked property of the "filter by" CheckBox to a bool property of your view model and filter the LogEntries source collection when the IsChecked source property is set, e.g.:
public class ViewModel : INotifyPropertyChanged
{
private bool _isChecked;
public bool IsChecked
{
get { return _isChecked; }
set
{
_isChecked = value;
OnPropertyChanged();
//filter collection:
LogEntries = allLogEntries.Where(x => x.ServerTime == SelectedDate).ToList();
}
}
private List<LogEntry> _logEntries;
public List<LogEntry LogEntries
{
get { return _logEntries; }
set
{
_logEntries = value;
OnPropertyChanged();
}
}
//...
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
I doubt there is a way to do it in XAML only, since filtering requires you to specify how to filter (for example by creating a predicate).
What I recommend is getting an ICollectionView from your ItemsSource (which I assume is an ObservableCollection) and set it's Filter property.
Check out this answer for more details : Filter a DataGrid in WPF
I have two checkboxes. When I check one, I want the other to uncheck if it's currently checked. Both can be unchecked. Currently, I can check one, but when I check the other, both of them uncheck. I'm having trouble figuring out why.
Note: I want to do this all in the .xaml if possible. I've been messing with data triggers to get the proper effect with no luck so far.
<CheckBox Name="SYOChkBox">
<CheckBox.Style>
<Style TargetType="CheckBox">
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked, ElementName=UTMChkBox}"
Value="True">
<Setter Property="IsChecked"
Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
</CheckBox>
<CheckBox Name="UTMChkBox">
<CheckBox.Style>
<Style TargetType="CheckBox">
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked, ElementName=SYOChkBox}"
Value="True">
<Setter Property="IsChecked"
Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
</CheckBox>
You should really be using a ViewModel to do this (I'll leave a snippet on how you could do it using a VM at the end of the post).
Anyway, here's what I was able to do with XAML only
Using CheckBox
The problem with using this approach is that when both are checked, both of the triggers will be triggered (??), and both will be unchecked. So, I guess what you can do is this:
CheckBox X will normally uncheck when you check CheckBox Y, but CheckBox X just won't check while CheckBox Y is checked.
<CheckBox Name="SYOChkBox">
<CheckBox.Style>
<Style TargetType="CheckBox">
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsChecked, ElementName=UTMChkBox}" Value="True" />
<Condition Binding="{Binding IsChecked, ElementName=SYOChkBox}" Value="True" />
</MultiDataTrigger.Conditions>
<Setter Property="IsChecked" Value="False" />
</MultiDataTrigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
</CheckBox>
<CheckBox Name="UTMChkBox" />
Using RadioButton
I know it isn't what you asked, but as a sugestion, you could use a RadioButton, the code would be a lot cleaner:
<RadioButton>Option A</RadioButton>
<RadioButton>Option B</RadioButton>
<RadioButton>None</RadioButton>
Using ViewModel
XAML
<CheckBox IsChecked="{Binding IsChecked_CheckBoxA}" />
<CheckBox IsChecked="{Binding IsChecked_CheckBoxB}" />
Code-behind:
private bool _IsChecked_CheckBoxA;
private bool _IsChecked_CheckBoxB;
public bool IsChecked_CheckBoxA
{
get
{
return _IsChecked_CheckBoxA;
}
set
{
if (value)
IsChecked_CheckBoxB = false;
_IsChecked_CheckBoxA = value;
NotifyPropertyChanged();
}
}
public bool IsChecked_CheckBoxB
{
get
{
return _IsChecked_CheckBoxB;
}
set
{
if (value)
IsChecked_CheckBoxA = false;
_IsChecked_CheckBoxB = value;
NotifyPropertyChanged();
}
}
As others have said, it might not be doable in just xaml, and using a viewmodel is suggested. So here's what I have that works:
Xaml:
<CheckBox Name="SYOChkBox"
IsChecked="{Binding IsCheckedSYO}" />
<CheckBox Name="UTMChkBox"
IsChecked="{Binding IsCheckedUTM}" />
Viewmodel:
private bool _isCheckedSYO;
public bool IsCheckedSYO
{
get { return _isCheckedSYO; }
set
{
if (true == value) //if checked
{
IsCheckedUTM = false; //uncheck the UTM check box
_isCheckedSYO = value;
}
else
{
_isCheckedSYO = value;
}
OnPropertyChanged("IsCheckedSYO");
}
}
private bool _isCheckedUTM;
public bool IsCheckedUTM
{
get { return _isCheckedUTM; }
set
{
if (true == value) //if checked
{
IsCheckedSYO = false; //uncheck the SYO checkbox
_isCheckedUTM = value;
}
else
{
_isCheckedUTM = value;
}
OnPropertyChanged("IsCheckedUTM");
}
}
I am working on an application that implements the MVVM design pattern with DataAnnotations. The application is a dynamically generated list of pages. On one of those pages, I have 10 required fields with 2 yes/no radio buttons. Those 10 fields are divided into two groups and each group is wwapped with a border tag. Each border's visibility is bound with the radio buttons for hidden/visible.
My question is if yes was selected and the related 5 required text boxes are displayed how can i set the ValidatesOnDataErrors to false/true and clear the text boxes values of the other hidden required TextBoxes?
Here is a code Snippet.
thanks
<Border>
<Border.Style>
<Style>
<Setter Property="Border.Visibility" Value="Hidden"></Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=PresentlyEmployed_yes, Path=IsChecked}"
Value="True">
<Setter Property="Border.Visibility" Value="Visible"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<Grid Height="Auto" Width="Auto">
<Label Name="JobTitle"
Content="{x:Static properties:Resources.JobTitlelbl}" />
<TextBox Name="JobTitle" Text="{Binding JobTitle, Mode=TwoWay,
ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}">
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Setter Property="Text" Value="{Binding PrimaryInsuredBusinessDuties, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged, IsAsync=True}" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=PresentlyEmployed_yes, Path=IsChecked}"
Value="True">
<Setter Property="Text" Value="{Binding JobTitle, Mode=TwoWay,
ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}" />
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=PresentlyEmployed_yes, Path=IsChecked}"
Value="False">
<Setter Property="Text" Value="{Binding JobTitle, Mode=TwoWay,
ValidatesOnDataErrors=False, UpdateSourceTrigger=PropertyChanged}"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</Grid>
</Border>
Try setting the Validation.Template to {x:Null} if it shouldn't show the Validation Error
<StackPanel>
<ListBox x:Name="MyListBox" SelectedIndex="0">
<ListBoxItem>Validate Value 1</ListBoxItem>
<ListBoxItem>Validate Value 2</ListBoxItem>
</ListBox>
<TextBox Text="{Binding Value1, ValidatesOnDataErrors=True}">
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<DataTrigger Binding="{Binding SelectedIndex, ElementName=MyListBox}" Value="1" >
<Setter Property="Validation.ErrorTemplate" Value="{x:Null}" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
<TextBox Text="{Binding Value2, ValidatesOnDataErrors=True}">
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<DataTrigger Binding="{Binding SelectedIndex, ElementName=MyListBox}" Value="0" >
<Setter Property="Validation.ErrorTemplate" Value="{x:Null}" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</StackPanel>
Sure, here is how my validationbase class looks like (Simplified)
public class ValidationViewModelBase : ViewModelBase, IDataErrorInfo, IValidationExceptionHandler
{
private Dictionary<string, Func<ValidationViewModelBase, object>> _propertyGetters;
private Dictionary<string, ValidationAttribute[]> _validators;
/// <summary>
/// Gets the error message for the property with the given name.
/// </summary>
/// <param name="propertyName">Name of the property</param>
public string this[string propertyName]
{
IList<string> fieldsNames = new List<string>();
{
if (propertyName == "PresentlyEmployed")
{
//if its true then
fieldsNames.Add("JobTitle");
AddFieldsValidation(fieldsNames);
}else{
fieldsNames.Add("EmploymentAddress");
RemoveValidation(fieldsNames);
}
if (this.propertyGetters.ContainsKey(propertyName))
{
var propertyValue = this.propertyGetters[propertyName](this);
var errorMessages = this.validators[propertyName]
.Where(v => !v.IsValid(propertyValue))
.Select(v => v.ErrorMessage).ToArray();
return string.Join(Environment.NewLine, errorMessages);
}
return string.Empty;
}
/// <summary>
/// Gets an error message indicating what is wrong with this object.
/// </summary>
public string Error
{
get
{
var errors = from validator in this.validators
from attribute in validator.Value
where !attribute.IsValid(this.propertyGetters[validator.Key](this))
select attribute.ErrorMessage;
return string.Join(Environment.NewLine, errors.ToArray());
}
}
}
/// <summary>
/// Gets the number of properties which have a validation attribute and are currently valid
/// </summary>
public int ValidPropertiesCount
{
get
{
var query = from validator in this.validators
where validator.Value.All(attribute => attribute.IsValid(this.propertyGetters[validator.Key](this)))
select validator;
var count = query.Count() - this.validationExceptionCount;
return count;
}
}
}
/// <summary>
/// Gets the number of properties which have a validation attribute
/// </summary>
public int TotalPropertiesWithValidationCount
{
get
{
return this.validators.Count();
}
}
public ValidationViewModelBase()
{
this.validators = this.GetType()
.GetProperties()
.Where(p => this.GetValidations(p).Length != 0)
.ToDictionary(p => p.Name, p => this.GetValidations(p));
this.propertyGetters = this.GetType()
.GetProperties()
.Where(p => this.GetValidations(p).Length != 0)
.ToDictionary(p => p.Name, p => this.GetValueGetter(p));
}
private ValidationAttribute[] GetValidations(PropertyInfo property)
{
return (ValidationAttribute[])property.GetCustomAttributes(typeof(ValidationAttribute), true);
}
private Func<ValidationViewModelBase, object> GetValueGetter(PropertyInfo property)
{
return new Func<ValidationViewModelBase, object>(viewmodel => property.GetValue(viewmodel, null));
}
private int validationExceptionCount;
public void ValidationExceptionsChanged(int count)
{
this.validationExceptionCount = count;
this.OnPropertyChanged("ValidPropertiesCount");
}
would like to know how to highlight a gridview row if some object value is greater than another ? (Consider the gridview to be bind to an observablecollection)
Thanks.
Put a property on the class of the items in your observable collection that will get set to true/false based on the comparison you need. Then you should be able to bind to this property in a DataTrigger for a Style on the DataGrid's ItemContainerStyle.
Try this...
<DataGrid ItemsSource="{Binding YourObservableCollection}" >
<DataGrid.ItemContainerStyle>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding YourShouldHighlightProperty}" Value="True">
<Setter Property="Control.Background" Value="Red"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.ItemContainerStyle>
</DataGrid>
You will need to implement a converter to do a > comparison but this shows a highlight
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsParent}" Value="True">
<Setter Property="Background" Value="Gainsboro" />
</DataTrigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
Use the following code.It wored for me.
private void DataGrid_LoadingRow(object sender, DataGridRowEventArgs e)
{
try
{
foreach (loadtodatagrid item in gridview.ItemsSource)
{
var row = gridview.ItemContainerGenerator.ContainerFromItem(item) as DataGridRow;
if (item.item1== item.item2)
{
row.Background = new SolidColorBrush(Colors.Yellow);
}
else
{
// row.Background = new SolidColorBrush(Colors.Green);
}
}
}
catch(Exception ep)
{
//do nothing....
}
LoadingRow="DataGrid_LoadingRow" to datagrid in WPF
I have the following xaml inside a text box element that is part of a combo box item template. The combobox's items source is set to a list of objects that have a boolean property AcceptsInput everything works great but I can't get this trigger to fire do I have to do something else.
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<DataTrigger Binding="{Binding AcceptsInput}" Value="False" >
<Setter Property="Visibility" Value="Hidden"> </Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
Are you correctly implementing INotifyPropertyChanged in the viewmodel class with the AcceptsInput property?
It should look something like this:
public class MyClass: INotifyPropertyChanged
{
private bool _acceptsInput;
public bool AcceptsInput
{
get { return _acceptsInput; }
set
{
_acceptsInput = value;
OnPropertyChanged("AcceptsInput");
}
}
...
}