self tooltip in listbox - wpf

I have a listox that bind to some ObservableCollection<string>, I want tooltip for each row to be the row content.
I have tried like:
<ListBox ItemsSource="{Binding MyList}">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="ToolTip" Value="{Binding MyList}"/>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
In ViewModel:
public ObservableCollection<string> _myList;
public ObservableCollection<string> MyList
{
get { return _myList; }
set
{
if (value != this._myList)
_myList = value;
RaisePropertyChanged("MyList");
}
}
but it doesn't shows the tooltip

DataContext of ListBoxItem is an element from MyList.
<ListBox ItemsSource="{Binding MyList}">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="ToolTip" Value="{Binding}"/>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>

Related

Change Cell Format of a WPF Gridcell based on the Value of an other Cell Value

I am starting to get involved with WPF MVVM.
Now in a grid I need to format the appearance of a cell based on the value in another cell.
Current Grid Example 1
The values in 'SAP No.' should appear red if the value 'DoppelSAPOrder' is true in the viewmodel. The value 'DoppelSAPOrder' is not displayed in the grid.
The grid is on a UserControl
<UserControl...
<UserControl.DataContext>
<model:MainVM />
</UserControl.DataContext>
<Grid>
<datagrid:ThemedDataGrid HorizontalAlignment="Stretch" Height="auto" VerticalAlignment="Stretch" Width="auto"
x:Name="DataGrid" ItemsSource="{Binding Tickets}" AutoGenerateColumns="False"
IsReadOnly="True"
ClipboardCopyMode="IncludeHeader" SelectedItem="{Binding SelectedTicket}"...
<datagrid:ThemedDataGrid.Columns>
<datagrid:ThemedDataGrid.Columns>
<DataGridTextColumn Binding="{Binding CrmTicketId}" Header="Ticket Nr." Width="90">
<DataGridTextColumn.ElementStyle>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="HorizontalAlignment" Value="Center" />
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding SapAuftrag}" Header="SAP Nr." Width="85">
<DataGridTextColumn.ElementStyle>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="Foreground" Value="{Binding ???, Converter={StaticResource SomeBoolToBrushConverter-ToDo}}"/>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
The grid is bound to a List.
public class SimpleTicketDTO : BaseDTO
{
public string Kategorie { get; set; }
public string Status { get; set; }
public string PersonFirmaName { get; set; }
public bool HatSAPOrder { get; set; }
public string SapAuftrag { get; set; }
public bool DoppelSAPOrder { get; set; }
...
I cannot now access the property 'DoppelSAPOrder ' of the Row in the ElementStyle :-/
Can someone show me how to address this problem?
You want to bind TextBlock property in element style to its' parent row binded object and it can be achieved with the RelativeSource property of the binding like this:
<DataGridTextColumn Binding="{Binding SapAuftrag}" Header="SAP Nr." Width="85">
<DataGridTextColumn.ElementStyle>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="Foreground" Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}}, Path =Item.DoppelSAPOrder,Converter={StaticResource IsObsoleteToTextDecorationsConverter}}"/>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>

WPF Custom Control library : trying to bind to templatedparent property from a keyed resource, ancestor way fails

How can I make this custom control library binding work? The code is heavily simplified for the sake of brevity, the Listview chooses its current View among several Views thank to a Datatrigger in its style, I'm only showing one for the sake of brevity. The failing binding is the GridViewColumn.Width one
Themes/Generic.Xaml :
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfCustomControlLibrary1">
<DataTemplate x:Key ="myDataTemplate">
<!--This visibility binding works-->
<CheckBox Width="16" Height="16" x:Name="ItemCheckbox"
Visibility="{Binding CheckBoxVisibility,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type local:CustomControl1}}}" />
</DataTemplate>
<GridView x:Name="myView" x:Key="myView" x:Shared="False">
<!--this width binding fails : Cannot find source for binding-->
<GridViewColumn x:Name="NameColumn" Header="Name"
CellTemplate="{StaticResource myDataTemplate}"
Width="{Binding NameColumnWidth,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type local:CustomControl1}}}" />
</GridView>
<Style TargetType="{x:Type local:CustomControl1}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:CustomControl1}">
<ListView ItemsSource="{Binding
RelativeSource={RelativeSource TemplatedParent},
Path=ItemsSource}" >
<ListView.Style>
<Style TargetType="{x:Type ListView}">
<Style.Triggers>
<DataTrigger Binding="{Binding
RelativeSource={RelativeSource TemplatedParent},
Path=ViewDetails}" Value="True">
<Setter Property="View"
Value="{StaticResource myView}" />
</DataTrigger>
<DataTrigger Binding="{Binding
RelativeSource={RelativeSource TemplatedParent},
Path=ViewDetails}" Value="False ">
<Setter Property="View"
Value="{StaticResource myView2}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ListView.Style>
</ListView>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
CustomControl1.cs
public class CustomControl1 : Control
{
//these properties should be DependencyProperties, simplified here for the sake of brevity
ObservableCollection<Item> items;
public object ItemsSource { get => items; }
//this is the property I fail to bind to
public double NameColumnWidth { get => this.Width; }
//this one works
public Visibility CheckBoxVisibility { get => Visibility.Visible; }
static CustomControl1()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomControl1),
new FrameworkPropertyMetadata(typeof(CustomControl1)));
}
}
Edit:
I actually ended up changing my approach and instead of binding the column width to a Property in the templated parent I made it auto resizing to its content by subclassing GridView. This is an overall better solution. If anyone finds a solution to this binding puzzle I'll still be happy to test and accept it.
public class AutoAdjustingGridView: GridView
{
protected override void PrepareItem(ListViewItem item)
{
base.PrepareItem(item);
foreach(var column in Columns)
{
if (double.IsNaN(column.Width))
column.Width = column.ActualWidth;
column.Width = double.NaN;
}
}
}

Bind to HierarchicalDataTemplate DataContext from nested DataTemplate

I have my treeview with 2 level nested levels.
<TreeView ItemsSource="{Binding Project.Tables}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type TableModel}" ItemsSource="{Binding Columns}">
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type ColumnModel}">
</DataTemplate>
</TreeView.Resources>
</TreeView>
My binding source with tables:
public class ProjectModel : PropertyChangedBase
{
private BindableCollection<TableModel> _tables;
public BindableCollection<TableModel> Tables
{
get { return _tables; }
set
{
if (_tables != value)
{
_tables = value;
NotifyOfPropertyChange();
}
}
}
}
public class TableModel
{
public BindableCollection<ColumnModel> Columns { get; set; }
public bool IsSelected
{
get;
}
}
I tried to use next binding:
<DataTemplate DataType="{x:Type projectModels:ColumnModel}">
<StackPanel Orientation="Horizontal">
<StackPanel.Resources>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TreeViewItem}}, Path=DataContext.IsSelected }" Value="True">
<Setter Property="Foreground" Value="{StaticResource GreyBrush3}"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Resources>
<TextBlock Text="{Binding ColumnName}">
</TextBlock>
</StackPanel>
</DataTemplate>
Inside DataTemplate I want to have binding to DataContext of HierarchicalDataTemplate. I already tried different AncestorLevels and it did not work, any ideas how can I do it?

How to bind ItemsControl in DataGrid behavior

I have this custom vertical scrollbar that is defined in UserControl.Resources, it has a ItemsControl in it called 'ItemsSelected'.
What I would to do is bind it to the DependencyProperty ItemsControlObject in behavior DataGridSelectionChanged. The example binding does not work but shows what I'd like to achieve. What am I missing to bind ItemsSelected?
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'ElementName=ItemsSelected'.
BindingExpression:(no path);
DataItem=null;
target element is 'DataGridSelectionChanged' (HashCode=43407976);
target property is 'ItemsControlObject' (type 'ItemsControl')
<UserControl>
<UserControl.Resources>
<ControlTemplate x:Key="VerticalScrollBar" TargetType="{x:Type ScrollBar}">
<Grid>
...
<!-- BEGIN -->
<ItemsControl Name="ItemsSelected" VerticalAlignment="Stretch"
ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.MarkerCollection}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Rectangle Fill="Gray" Width="18" Height="4"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Top" Value="{Binding}" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<!-- END -->
</Grid>
</ControlTemplate>
<Style TargetType="{x:Type DataGrid}" >
<Style.Resources>
<Style x:Key="{x:Type ScrollBar}" TargetType="{x:Type ScrollBar}">
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="OverridesDefaultStyle" Value="true" />
<Style.Triggers>
<Trigger Property="Orientation" Value="Horizontal">
<Setter Property="Width" Value="Auto" />
<Setter Property="Height" Value="18" />
<Setter Property="Template" Value="{StaticResource HorizontalScrollBar}" />
</Trigger>
<Trigger Property="Orientation" Value="Vertical">
<Setter Property="Width" Value="18" />
<Setter Property="Height" Value="Auto" />
<Setter Property="Template" Value="{StaticResource VerticalScrollBar}" />
</Trigger>
</Style.Triggers>
</Style>
</Style.Resources>
</Style>
</UserControl.Resources>
<Grid Name="gridUsers" Background="Transparent">
<DockPanel>
<DataGrid Name="GenericDataGrid">
<i:Interaction.Behaviors>
<helpers:DataGridSelectionChanged ItemsControlObject="{Binding ElementName=ItemsSelected}" />
</i:Interaction.Behaviors>
<DataGrid.Columns>
...
</DataGrid.Columns>
</DataGrid>
</DockPanel>
</Grid>
</UserControl>
[EDIT]
public static class ScrollBarMarkers
{
public static readonly DependencyProperty MarkersSelectedCollectionProperty =
DependencyProperty.RegisterAttached("MarkersSelectedCollection", typeof(ObservableCollection<double>), typeof(ScrollBarMarkers), new PropertyMetadata(null));
public static ObservableCollection<double> GetMarkersSelectedCollection(DependencyObject obj)
{
return (ObservableCollection<double>)obj.GetValue(MarkersSelectedCollectionProperty);
}
public static void SetMarkersSelectedCollection(ItemsControl obj, ObservableCollection<double> value)
{
obj.SetValue(MarkersSelectedCollectionProperty, value);
}
}
Implementing things like this stops the need for binding the actual ItemsControl
Here is the binding:
<ItemsControl ItemsSource="{Binding Source={x:Static helpers:MyClass.Instance}, Path=SelectedMarkers}">
Here is the class with singleton pattern
public class MyClass : INotifyPropertyChanged
{
public static ObservableCollection<double> m_selectedMarkers = new ObservableCollection<double>();
public ObservableCollection<double> SelectedMarkers
{
get
{
return m_selectedMarkers;
}
set
{
m_selectedMarkers = value;
NotifyPropertyChanged();
}
}
private static MyClass m_Instance;
public static MyClass Instance
{
get
{
if (m_Instance == null)
{
m_Instance = new MyClass();
}
return m_Instance;
}
}
private MyClass()
{
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}

WPF, XAML: How to style a ListBoxItem using binding on property of ListBox ItemsSource object?

I have a ListBox which is bound to ObservableCollection of LogMessages.
public ObservableCollection<LogMessage> LogMessages { get; set; }
public LogMessageData()
{
this.LogMessages = new ObservableCollection<LogMessage>();
}
Each Message has two parameters:
public class LogMessage
{
public string Msg { get; set; }
public int Severity { get; set; }
//code cut...
}
ListBox is getting filled with those Items, and I need to color-code (change a background color of ListBoxItem) list depending on a Severity parameter of a LogMessage item.
Here's what I have now in XAML of user control showing the log:
<UserControl.Resources>
<AlternationConverter x:Key="BackgroundSeverityConverter">
<SolidColorBrush>Green</SolidColorBrush>
<SolidColorBrush>Yellow</SolidColorBrush>
<SolidColorBrush>Red</SolidColorBrush>
</AlternationConverter>
<Style x:Key="BindingAlternation" TargetType="{x:Type ListBoxItem}">
<Setter Property="Background"
Value="{Binding RelativeSource={RelativeSource TemplatedParent},
Path=Severity,
Converter={StaticResource BackgroundSeverityConverter}}"/>
</Style>
<DataTemplate x:Key="LogDataTemplate">
<TextBlock x:Name="logItemTextBlock" Width="Auto" Height="Auto"
Text="{Binding Msg}"/>
</DataTemplate>
</UserControl.Resources>
and an actual ListBox:
<ListBox IsSynchronizedWithCurrentItem="True"
ItemTemplate="{DynamicResource LogDataTemplate}"
ItemsSource="{Binding LogFacility.LogMessages}"
x:Name="logListBox" Grid.Row="1"
ItemContainerStyle="{StaticResource BindingAlternation}" />
The AlternationConverter is used because the Severity parameter of message is of type Int (0..3), and we can easily switch between styles using that one.
The concept is clear, but so far it does not work for me. The Background color of ListBoxItem did not change.
Use ItemContainerStyle:
<ListBox ItemsSource="{Binding LogMessages}">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Background" Value="{Binding Severity, Converter={StaticResource YourBackgroundConverter}}"/>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
Like Bojan commented, it's the RelativeSource which shouldnt be there.
Use {Binding Path=Severity, Converter={StaticResource BackgroundSeverityConverter}} when you're binding to your data object. RelativeSource.TemplatedParent is for binding to ListBoxItem.
Additionally, something of a pet peeve of mine, you could consider using triggers, for example:
<Style x:Key="BindingAlternation" TargetType="{x:Type ListBoxItem}">
<Style.Triggers>
<DataTrigger Binding="{Binding Severity}" Value="1">
<Setter Property="Background" Value="Green"/>
</DataTrigger>
<DataTrigger Binding="{Binding Severity}" Value="2">
<Setter Property="Background" Value="Yellow"/>
</DataTrigger>
<!-- etc.. -->
</Style.Triggers>
<Style x:Key="BindingAlternation" TargetType="{x:Type ListBoxItem}">
But that's just a personal preference....what you have there should work fine if you fix the binding.

Resources