For example, i have a ListBox with my Employees, via binding.
I want to color all the Employees that earn more than 10,000$.
Thanks
The most straightforward way is to implement a property in the Employee that returns a string value, and then test the string value in a style's data trigger. For instance:
public string SalaryRange
{
get
{
if (salary <= LowRange)
{
return "Low";
}
if (salary <= MiddleRange)
{
return "Middle";
}
return "High";
}
}
Then, in XAML, implement a style that uses a data trigger, e.g.:
<Style TargetType = "ListBoxItem">
<Style.Triggers>
<DataTrigger Binding="{SalaryRange}" Value="Low">
<Setter Property="Foreground" Value="AliceBlue"/>
</DataTrigger>
<DataTrigger Binding="{SalaryRange}" Value="Middle">
<Setter Property="Foreground" Value="Fucschia"/>
</DataTrigger>
<DataTrigger Binding="{SalaryRange}" Value="Middle">
<Setter Property="Foreground" Value="Goldenrod"/>
</DataTrigger>
</Style.Triggers>
</Style>
A lot of people learn about value converters and get all enamored with the idea of using them for this kind of thing. But if you're using MVVM (which you should be), the above is really much cleaner: it's clear what the property does, it's clear what the style does, it's easy to test, and there's no third piece of code hiding in a separate file that's doing all of the translation.
Related
I'm making a simple application with WPF and binding one of the windows to a view model. The view model needs a few references at run-time to get the data it needs, so my view model has a constructor with parameters. I also have a parameter-less constructor to use during design to test the look of the window.
The problem I'm running in to is that whenever a constructor with parameters exists visual studio displays the error:
'The type "SettingsViewModel" does not include any accessible constructors.'
If I comment out the run-time constructor then no errors occur and everything works as expected.
Additionally, I've tried making the run-time constructor private and exposing it through a factory method, which had no effect. I've tried commenting out code-behind code referencing the second constructor. I've also tried cleaning the solution, restarting visual studio, then rebuilding.
So far the only thing I have found to work is commenting out the run-time constructor and any references to it. Also just to be clear I am not using the SettingsViewModel instance for anything yet, at this point I'm just trying to define it as a resource.
SettingsWindow.xaml:
<Window.Resources>
<local:SettingsViewModel x:Key="DevelopTimeModel" />
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="BorderBrush" Value="Gray"></Setter>
<Setter Property="BorderThickness" Value="1"></Setter>
<Setter Property="Padding" Value="2 3"></Setter>
<Style.Triggers>
<Trigger Property="ItemsControl.AlternationIndex" Value="0">
<Setter Property="Background" Value="#00000000"></Setter>
</Trigger>
<Trigger Property="ItemsControl.AlternationIndex" Value="1">
<Setter Property="Background" Value="LightGray"></Setter>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
SettingsWindow.xaml.cs
public SettingsWindow(Autodesk.Revit.UI.UIApplication app)
{
InitializeComponent();
SettingsViewModel viewModel = SettingsViewModel.GetRuntimeViewModel(app, this);
this.DataContext = viewModel;
MainListBox.SelectionChanged += (o, e) => viewModel.RefreshCommands();
}
SettingViewModel.cs
public SettingsViewModel()
{
//creation of design-time data here
}
public static SettingsViewModel GetRuntimeViewModel(UIApplication app, SettingsWindow settingsWindow)
{
return null;// new SettingsViewModel(app, settingsWindow);
}
private SettingsViewModel(UIApplication app, SettingsWindow settingsWindow)
{
ContextRouter = new RevitContextRouter(app, this.ToString());
members.PropertyChanged += (o, e) => NotifyPropertyChanged("Members." + e.PropertyName);
Commands = new SettingsCommands(this);
LoadSettings();
SettingsWindowRef = settingsWindow;
settingsWindow.Closed += (s,e) => SaveSettings() ;
}
Any help getting rid of this error message and similar ones when they come up would be appreciated.
I have a DataGrid that's bound to Datatable, and I want to uniquely identify rows in the DataTable using the ID, but I don't want it to be shown in the DataGrid
What I reached so far by searching and excluding:
Data columns are not predefined, so, I have to use AutoGenerateColumns=True, hence, I can't define the columns manually and set the Visibility property to False.
I can't use List or ObservableCollection to define private ID member, because the data are dynamic.
I am following MVVM so, I can't use AutoGeneratingColumns event handler directly and can't expose the View to the ViewModel.
The closest I get to an answer is using DataTrigger to set Visibility to False using CellStyle, but it just hid the cells, not the entire column, and I also tried it for DataGridColumnHeader and it didn't work:
code:
<Style x:Key="ColumnStyle" TargetType="DataGridColumnHeader">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=Value}" Value="id">
<Setter Property="Visibility" Value="Hidden"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
How to do it while maintaining the previous conditions?
Thanks in advance
EDIT:
I fixed the code for DataGridColumnHeader using Path=Column.Header which doesn't make sense to me but it's irrelevant; Still, there and empty column standing there, with no idea how to remove it.
It sounds like you want to track the selected Item. If you want to track the "selected Element", you have to use a CollectionView.
WPF controls do not direcly bind to collections. They bind to a CollectionView. And if you do not give them one, they will create one themself from whatever collection you hand them. If you want sorting, filtering, ordering and selection tracking, CollectionView is the droid you are looking for:
https://msdn.microsoft.com/en-us/library/system.windows.data.collectionview.aspx
Just take control of it's creation and expose it (rather then the raw collection).
I found a solution by applying this style:
<Style x:Key="ColumnStyle" TargetType="DataGridColumnHeader">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=Value}" Value="id">
<Setter Property="Visibility" Value="Hidden"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
to DataGridCell and to DataGridColumnHeader and allocating the column in the end of the table this removed the empty column from the middle of the table.
Sorry for my bad english. I want to bind a BorderBrush of ListBoxItem depending on the bool value of object.
I have a Directory class, that has IsForCopy and IsCutted bool props.
So, if IsForCopy is true, then BorderBrush = Red, if IsCutted is true, then BorderBrush = Blue. How can I do this?
ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}" BasedOn="{StaticResource {x:Type ListBoxItem}}">
<EventSetter Event="MouseDoubleClick" Handler="SecondListBoxItem_MouseDoubleClick"/>
<Setter Property="BorderBrush" Value="What do I should type there?"></Setter>
</Style>
</ListBox.ItemContainerStyle>
You need a class that implements IValueConverter. Then in the convert method you can specify how you want to map a bool to a brush any way you want. And every binding has a converter attribute which you can set to this class that implemented IValueConverter.
I'm having an issue when trying to do something which should be as easy as. I've attempted to use a Trigger based on a DependencyProperty or a DataTrigger - I can't get either to work.
XAML for the trigger is:
<Style x:Key="FileWatchButton" BasedOn="{StaticResource {x:Type Button}}" TargetType="{x:Type Button}">
<Style.Triggers>
<Trigger Property="Main:Main.XmlFilesAvailableForLoading" Value="True">
<Setter Property="Background" Value="Red" />
</Trigger>
</Style.Triggers>
</Style>
And the associated code-behind is:
public static readonly DependencyProperty XmlFilesAvailableForLoadingProperty =
DependencyProperty.Register("XmlFilesAvailableForLoading", typeof(bool), typeof(Main));
public bool XmlFilesAvailableForLoading
{
get
{
try
{
return (bool)this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.DataBind,
(System.Windows.Threading.DispatcherOperationCallback)delegate { return GetValue(XmlFilesAvailableForLoadingProperty); },
XmlFilesAvailableForLoadingProperty);
}
catch (Exception)
{
return (bool)XmlFilesAvailableForLoadingProperty.DefaultMetadata.DefaultValue;
}
}
set
{
this.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.DataBind,
(System.Threading.SendOrPostCallback)delegate{ SetValue(XmlFilesAvailableForLoadingProperty, value); }, value);
}
}
Basically the dp is being set correctly by the presenter (it's based on a FileSystemWatcher class looking for one or more files) but the Trigger is not being fired. Is this a threading issue?
Thanks.
It's not clear if the code is complete, but it looks like the Property path in your trigger may be wrong. Does the button being styled have a Main property? I am guessing not; it looks like you are trying to trigger on a property of a different element, called Main -- is that right?
In any case, the namespace prefix is not required. If the button has a property named Main, then you can address this directly; if it doesn't, then the prefix won't help you.
My guess is that you probably need a DataTrigger whose binding refers to the Main element:
<local:Main Name="MyMain" ... /> <!-- this has the XmlFilesAvailableForLoading property -->
<DataTrigger Binding="{Binding XmlFilesAvailableForLoading, ElementName=MyMain}"
Value=True>
<Setter Property="Background" Value="Red" />
</DataTrigger>
On an unrelated note, you should have any non-boilerplate implementation in your DP getter and setter. Remember that the binding and styling system will bypass the getter and setter and talk directly to the underlying storage. So I'd strongly advise changing these back to just plain GetValue and SetValue calls.
I have a ListBox bound to a list of Items (for arguement, lets say its got a string and two dates Entered and Done).
I would like to make the background color of items in the ListBox have a gray color if the Done DateTime is != DateTime.MinValue.
Edit:
Should I make a converter? and convert DateTime to a Brush based on the value of the DateTime?
Is something like this my best option? or is there a simple Xaml snippet I could use?
[ValueConversion(typeof(DateTime), typeof(Brush))]
class MyConverter : IValueConverter
{
...
}
A ValueConverter would work. Another option would be to use a DataTrigger in the style of ListBoxItem. Maybe something like this:
<Style x:Name="MinDateTimeListBoxStyle" TargetType="ListBoxItem">
<Style.Triggers>
<Setter Property="Background" Value="Gray" />
<DataTrigger Binding="{Binding Path=Done}"
Value="{x:Static sys:DateTime.MinValue}">
<Setter Property="Background" Value="White" />
</DataTrigger>
</Style.Triggers>
</Style>
This will set the background to Gray when the value of Done isn't DateTime.MinValue. I don't think there is a way to do a not equals comparison in a trigger, so it sets the background to Gray by default, and only changing it back to white if Done hasn't changed yet. It would probably be better to use the correct color for the background instead of white (maybe get the value of the parent's background?), but this should give you something to start with.
Update: To apply this style to the items of only certain ListBoxes, give the style a name and set the ItemContainerStyle as appropriate:
<ListBox x:Name="StyledListBox"
ItemContainerStyle="{StaticResource MinDateTimeListBoxStyle}" />
<ListBox x:Name="NormalListBox" />