I have a ListView with 3 columns:
<ListView ItemsSource="{Binding ParamName}" HorizontalAlignment="Left" Height="109" Margin="10,87,0,0" VerticalAlignment="Top" ScrollViewer.HorizontalScrollBarVisibility="Disabled" Width="281">
<ListView.View>
<GridView>
<GridView.ColumnHeaderContainerStyle>
<Style TargetType="{x:Type GridViewColumnHeader}">
<Setter Property="IsEnabled" Value="False"/>
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="TextElement.Foreground" Value="Black"/>
</Trigger>
</Style.Triggers>
</Style>
</GridView.ColumnHeaderContainerStyle>
<GridViewColumn Header="Name" Width="60"/>
<GridViewColumn Header="Type" Width="60"/>
<GridViewColumn Header="Content" Width="156"/>
</GridView>
</ListView.View>
</ListView>
Currently, I have set ParamName as my ItemsSource, which will obviously not achieve the desired result this way.
In my ViewModel, I have ObservableCollections for each Column (ParamName for Name, ParamType for Type and ParamContent for Content). Those ObservableCollections are correctly filled and I am able to receive their data through the Binding, but I cannot fill the columns with their respective data.
I have thought of certain possible solutions, but none of them seem to work. What would be the best approach for this problem?
Here's how it looks like (left) and how it should look like (right):
Naming them after their Types might be a little bit confusing.
You have three Collections, but only one is binded. So all columns have same value.
Try to create one Collection containing all you need:
ObservableCollection<MyRow>
where MyRow is a Class or Struct with Properties you need.
If you already have these Collections - try to concatenate it to one major Collection and tell the GridColumns which Properties you wish to bind to each Column, but I'm not sure if it's possible with ObservableCollections - what if they have different length?
And you can still create your own Collection from these three - just parse it...
Related
I'm pretty new to WPF but I've read a lot on it as well as MVVM in the last couple if days.
My WPF displays a DataGrid with custom column templates (using the NumericUpDown Controls from Xceed WPF Toolkit). Three of the columns contain the decimal coordinates of a 3D vector. I use IDataErrorInfo to make sure the vector's length is never 0 (all three columns cannot be 0 at the same time). This is working fine so far, the cells are marked red when the validation fails, but I also want to show the error message in a tooltip or similar.
<DataGrid [...]>
<DataGrid.Columns>
[...]
<DataGridTemplateColumn Header="X" [...]>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<xctk:DecimalUpDown Value="{Binding PositionX, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, NotifyOnValidationError=True}">
</xctk:DecimalUpDown>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
[... and same thing for Y and Z]
</DataGrid.Columns>
</DataGrid>
This is where I'm stuck for a few hours now, so I hope you can help me here:
How can I show a error tooltip on custom template columns?
I've read through a lot of articles and threads on error tooltips but most of them are on plain TextBox or DataGridTextColumns and a tried a lot but could not make it working so far.
Most of them look something like this:
<Style x:Key="errorStyle" TargetType="{x:Type TextBox}">
<Setter Property="Padding" Value="-2"/>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="Background" Value="Red"/>
<Setter Property="ToolTip"
Value="{Binding RelativeSource={RelativeSource Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
from here:
https://msdn.microsoft.com/library/ee622975%28v=vs.100%29.aspx
or more exsamples:
Display validation error in DataGridCell tooltip
https://stackoverflow.com/a/7900510/5025424
https://harishasanblog.blogspot.de/2011/01/datagrid-validation-using.html
WPF data validation and display error message IDataErrorInfo and error templates
Nothing on this ever showed any Tooltip to me.
Can you give me a hint,
how this style trigger definition has to look for cells containing no TextBox,
where the definition has to be
and if the column needs the reference this definition in some way?
Thank you!
Set the Style property of the control to a Style with a trigger that sets the Tooltip property of the control in the CellTemplate if the attached Validation.HasError property returns true:
<DataGridTemplateColumn Header="X">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<xctk:DecimalUpDown Value="{Binding PositionX, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, NotifyOnValidationError=True}">
<xctk:DecimalUpDown.Style>
<Style TargetType="xctk:DecimalUpDown">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="ToolTip" Value="{Binding (Validation.Errors)[0].ErrorContent, RelativeSource={RelativeSource Self}}" />
</Trigger>
</Style.Triggers>
</Style>
</xctk:DecimalUpDown.Style>
</xctk:DecimalUpDown>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
Is it possible to bind a data grid column value to one property of the bound class specified in ItemsSource, but reference a different property in that class to style the cell?
Here is a code example:
<DataGrid ItemsSource="MyCollection">
<DataGridTextColumn Header="MyColumn" Binding={Binding ColumnText} />
<DataGridTextColumn Header="MyColumn2" Binding={Binding ColumnText2} />
</DataGrid>
Suppose that I have a two further properties (Enum or Brush) in the objects in MyCollection that I want to refer to to set the background colour of the respective cells; can this be done?
EDIT - I need to do this for multiple columns, each looking at a different property to ascertain the colour it should be; I have added a second column to the code example above.
I know I can set the style based on the value in ColumnText, but that is not what I need to do.
I have tried setting a style data trigger, but when I try to bind, I can only bind to something from the overall data context, not to another property within the object that is populating the current row.
Many thanks!
If I understand correctly, you are trying to set a cell background by a property from the Row model.
You can achieve this by setting a cell style, and set a DataTrigger to that style to bind to a property you want.
Example
You want to make each cell, that has the number 3 to be painted green:
<DataGrid ItemsSource="{Binding Rows}" AutoGenerateColumns="True">
<DataGrid.CellStyle>
<Style TargetType="{x:Type DataGridCell}">
<Style.Triggers>
<DataTrigger Binding="{Binding Number}" Value="3">
<Setter Property="Background">
<Setter.Value>
<SolidColorBrush Color="Green"/>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.CellStyle>
</DataGrid>
And, of course, you can change the Number property with your own property, and the value you are interested in.
If you want to do something more complex, like range of values and such, you should go with the conventional converters way.
Edit
If you want to make a different cell style for each column, you should explicitly set columns:
<DataGrid ItemsSource="{Binding Rows}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Number}">
<DataGridTextColumn.CellStyle>
<Style TargetType="{x:Type DataGridCell}">
<Style.Triggers>
<DataTrigger Binding="{Binding Number}" Value="3">
<Setter Property="Background">
<Setter.Value>
<SolidColorBrush Color="Green"/>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
One important thing to notice though,
You have to set AutoGenerateColumns="False" or else the columns will generate twice.
I'm using ListView along with the GridView for displaying the data in tabular format:
<ListView>
<ListView.View>
<GridView>
<GridViewColumn Header="Name" Width="120" DisplayMemberBinding="{Binding Name}" />
<GridViewColumn Header="Risk" Width="150" DisplayMemberBinding="{Binding RiskName}" />
</GridView>
</ListView.View>
I have to change the background color based on the RiskName. For example, if RiskName is "High" then the background would be Red, if RiskName is "Medium" then the background would be "Yellow", and so on.
I added the style along with trigger to set background based on the value,
<Trigger Property="Text" Value="High">
<Setter Property="Background" Value="Red"/>
</Trigger>
<Trigger Property="Text" Value="Medium">
<Setter Property="Background" Value="Yellow"/>
</Trigger>
It works fine, but in my case the text of the RiskName is not constant. The value comes dynamically. In WPF is there any way I can set the Value of trigger property dynamically which look something like this?
<Trigger Property="Text" Value="{Binding RiskName}">
<Setter Property="Background" Value="{Binding RiskBrush}"/>
</Trigger>
Any suggestion? If not then what is the other work around?
Something like this?
<converters:MyBrushLookupConverter x:Key="brushLookup" BrushDictionary="{Binding KeyedBrushes}" />
where MyBrushLookupConverter looks like
public class MyBrushLookupConverter : DependencyObject, IValueConverter
{
// This is a dependency property - dependency property gumf omitted for brevity
public Dictionary<string, Brush> BrushDictionary {get; set;}
// Convert method
public Convert(object value, ...)
{
return BrushDictionary[(string)value];
}
}
Use a Converter instead of a Trigger. Bind the Background to RiskName and write a converter that returns a Brush determined by the value of RiskName.
Link to MSDN for the interface you need to use IValueConverter - http://msdn.microsoft.com/en-us/library/system.windows.data.ivalueconverter%28v=vs.110%29.aspx
A link to a good tutorial on Converters: http://wpftutorial.net/ValueConverters.html
I'm using the MVVM Light Toolkit and I have a DataGrid bound to an ObservableCollection. There is only one text column displayed. I'd like the text of the cell to be Bold or Normal depending on a boolean that is inside the object displayed. I figured I could use RelayCommands but they only take 1 parameter and I need at least 2 to get the CellContent (the DataGridRowEventArgs and the DataGrid itself). I tried to fire a RelayCommand Execute delegate on "LoadingRow" event but with only one parameter I couldn't do it.
Here is the DataGrid in the XAML:
<DataGrid x:Name="dataGrid1" HorizontalAlignment="Left" Margin="112,34,0,8" Width="100" IsReadOnly="True" CanUserReorderColumns="False" CanUserResizeColumns="False" CanUserSortColumns="False" CanUserResizeRows="False" ItemsSource="{Binding CurrentNewsList}" AutoGenerateColumns="False" SelectedIndex="0">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Title}" MinWidth="92" Width="Auto" FontFamily="Segoe UI" Foreground="Black" FontWeight="{Binding CurrentNewsList[0].MyFont}"/>
</DataGrid.Columns>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<Custom:EventToCommand Command="{Binding NewsSelectedCommand}" CommandParameter="{Binding SelectedIndex, ElementName=dataGrid1}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</DataGrid>
I set the grid in Blend. Notice that the FontWeight is bound like "{Binding CurrentNewsList[0].MyFont}". Is it right ? I also tried "{Binding MyFont}" but both got the same result: No BOld :(
MyFont is set in the Object constructor with a boolean:
MyFont = newIsRead ? FontWeights.Normal : FontWeights.Bold;
Please help.
Thx
You could just use an implicit style and a trigger:
<DataGrid.Resources>
<Style TargetType="DataGridCell">
<Style.Triggers>
<DataTrigger Binding="{Binding MyBoolean}" Value="True">
<Setter Property="TextElement.FontWeight" Value="Bold" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.Resources>
(If you ever have more columns you could just use the styles on the column (ElementStyle & ElementEditingStyle) instead to limit the effect)
In a situation like that, I generally create a "model object" that is specifically for binding. So, instead of binding to an observable collection of "Customer", you bind to an observable collection of "CustomerModel", where the model object has a "CustomerName" property and then some other property that corresponds to the desired font (either an actual Font object, or some kind of enumeration that you parse via a value converter, if you don't want your VM layer knowing about view concerns). This model object could figure out what to make available depending on the boolean property you mention.
Here is how I managed to make it using H.B solution:
<DataGrid.Resources>
<Style x:Key="Style1" TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsRead}" Value="False">
<Setter Property="FontWeight" Value="Bold" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Title}" ElementStyle="{StaticResource ResourceKey=Style1}" />
</DataGrid.Columns>
I have a ListView that is has an ObservableCollection as its ItemsSource, and it has several columns. One of these is a State column which, depending on the current state of the item, shows a different message. Currently this is implemented as a basic string, and while it works it is far from pretty or userfriendly. I want to be able to vary the sort of output to more properly suit the state of the item.
I did do some research and know that I need to use a CellTemplate to affect the display, but all the different sorts of templates simply overwhelm me to the point where I can't figure out where to go next.
My code (excluding lots of other listview fluff) is as follows:
<ListView Name="itemsListView" ItemsSource="{Binding Source={StaticResource listingDataView}}" IsSynchronizedWithCurrentItem="True">
...
<ListView.View>
<GridView AllowsColumnReorder="true" ColumnHeaderToolTip="Item Information">
...
<GridViewColumn DisplayMemberBinding="{Binding Path=StatusMessage}" Width="283" Header="Status" HeaderContainerStyle="{StaticResource GVHeaderLeftAlignedStyle}" />
</GridView>
</ListView.View>
</ListView>
Yes, the items have hardcoded 'Status Message' that get updated alongside other properties that are actually relevant for the code, causing ugly duplication elsewhere in my code. (And yes, I know this is far from pretty, but I want to improve this too.) That property would be called ItemState as I am not all that creative.
So, my question is: how can I vary this column to have the most suitable display for the given state? Textual descriptions will do for many states, but some are rather lengthy and might profit from a text with a progress bar besides it, and maybe some sort of time remaining. Another state would profit from having a clickable hyperlink. In other words, I think I need at least 3 different CellTemplates.
I realize it is a rather open-ended question that largely suffers from the design mistakes of someone (=me) who has rather little experience with WPF, but that is exactly why I'm hoping someone experienced can set me straight with some basic code before I make an even worse mess of things than I have already. :)
You can use triggers to change the content of the cell, e.g.
<GridViewColumn Header="Status">
<GridViewColumn.CellTemplate>
<DataTemplate>
<ContentControl>
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding StateItem.HasError}" Value="True">
<Setter Property="ContentTemplate">
<Setter.Value>
<!-- Possibly create another contentcontrol which differentiates between errors -->
<DataTemplate>
<TextBlock Text="{Binding StateItem.Error}"
Foreground="Red"/>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding StateItem.HasError}" Value="False">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Image Source="Images/Default.ico"/>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
Code gets a bit crazy that way though if you branch it further but it's a way to do it.
Edit: The setters should set the ContentTemplate instead of the Content, apparently otherwise no new controls may be created and only one row shows the proper content since the content can only have one parent.