WPF toolkit charting : Customize DataPoint ToolTip - wpf

I would like to add a tooltip over the datapoint of a lineseries that shows both the X and Y values (independent and dependent values), rather than just the dependent value that appears by default. I am aware this is the same question as was written in this ticket - WPF toolkit charting : Customize datapoint label
However, I can't get the answer to work. There is a link to more detail that appears to be outdated.
My line series:
<DVC:Chart.Series>
<!--Have several lineseries that look like this, connected to a styling vm. Can add ToolTip=...-->
<VM:LineSeries x:Name="something"
Title="something"
DependentValuePath="Value"
IndependentValuePath="Key"
ItemsSource="{Binding something}"
DataPointStyle="{StaticResource DataPointBlue}"
>
</VM:LineSeries>
</DVC:Chart.Series>
My datapoints are styled here, but adding a setter property with any tooltip doesn't make a difference:
<UserControl.Resources>
<Style x:Key="DataPointBlue" TargetType="{x:Type DVC:DataPoint}">
<Setter Property="Background" Value="Blue"/>
</Style>
</UserControl.Resources>
I've tried adding this line of code from the above linked ticket in several places in a variety of ways, and I've tried using Binding in various ways, but nothing has hit the mark.
<ToolTipService.ToolTip>
<StackPanel Margin="2,2,2,2">
<ContentControl Content="{TemplateBinding IndependentValue}" />
<ContentControl Content="{TemplateBinding DependentValue}" />
</StackPanel>
</ToolTipService.ToolTip>
This has been a lot of trial and error that hasn't been making progress.

Related

Binding a DataGrid when already having a DataContext declared in a Window----WPF

I am running into an issue that I haven't been able to resolve and although I've looked at a few similar posts, I haven't found anything that explains my situation.
Basically I have a WPF Window:
<Window x:Class="NewGame">
<DataContext="{Binding RelativeSource={RelativeSource Self}}"/>
In the class I implement INotifyPropertyChanged to utilize bindings for some properties I have set up to update dynamically using XAML. For instance, I have a DB that has Primary, Secondary and Trim Colors(Hex codes) listed for teams, and the properties automatically will update based on changing the team. So I have the BorderBrush, Foreground and Backgrounds on various things auto-updating in XAML using:
<Foreground="{Binding Path=MyPrimColor}">
<Background={Binding Path=MySecColor}">
<BorderBrush={Binding Path=MyTrimColor}">
, etc...each could be any of the properties, it doesn't matter, those are all working fine.
Now, I have a DataGrid which I need to bind to a DataTable to display the players on the team, and that is where I have run into the issue. It tells me the "Items Collection must be empty before using Itemsource" and throws an exception. This was never an issue until I started using the databindings in XAML, when I had things set in code behind, everything worked fine, but I also know this isn't the way things are supposed to be done, which is why I want to have it working with the XAML data-bindings.
I created MyDT a property as DataTable, and when I try to bind
<DataGrid DataContext="{Binding Path=MyDT}">, it causes the Foreground and Background binding paths to try to bind to the Data.DataTable object as well which obviously throws an error.
I have seen some say I need to use <DataGrid.DataContext> inside the <datagrid> but I haven't gotten that to work either. I understand where the problem is coming from---I already have the bindings set at a higher level, but I just don't know how to fix it in XAML by only binding the DataGrid to the DT property while leaving the others to bind to the class level.
Here is the full code section in XAML:
<DataGrid x:Name="TeamRosterDT"
Height="400"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"
Foreground="{Binding Path=MyTrimColor}"
RowBackground="{Binding Path=MySecColor}"
AlternatingRowBackground="{Binding Path=MyPrimColor}"
CanUserAddRows="False"
CanUserDeleteRows="False"
CanUserResizeColumns="False"
ColumnWidth="Auto"
HorizontalScrollBarVisibility="Visible"
ItemsSource="{Binding}"
Opacity="0.8"
VerticalScrollBarVisibility="Visible"
Visibility="Hidden"
DataContext="{Binding Path=MyDT}">
<DataGridColumnHeader Style="{StaticResource DataGridHeaderStyle}" />
<DataGridCell HorizontalContentAlignment="Center" />
</DataGrid>
no need to change DataContext (DataContext="{Binding Path=MyDT}"), only bind ItemsSource (<DataGrid ItemsSource="{Binding Path=MyDT}"> or <DataGrid ItemsSource="{Binding Path=MyDT.DefaultView}">)
Exception is thrown because of incorrect items declaration (lines with DataGridColumnHeader, DataGridCell). They are added to Items list which is not supported when ItemsSource is set
I got the Exceptions resolved(I believe) by using:
<DataGrid.ColumnHeaderStyle>
<Style TargetType="DataGridColumnHeader">
<Setter Property="Background" Value="{Binding Path=MyPrimColor}" />
<Setter Property="Foreground" Value="{Binding Path=MyTrimColor}" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
</Style>
<DataGrid.ColumnHeaderStyle>
<DataGrid.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="HorizontalContentAlignment" Value="Center" />
</Style>
</DataGrid.CellStyle>
However, the DataTable does not update properly---when the XAML is initialized its an empty DB, which is then filled and filtered according to what team is selected. When a new team is selected, the DT is simply filtered, not refilled. However, since I suppose this technically isn't a "change" to the DT itself, it doesn't fire the OnPropertyChanged event. How can I get it to update properly using XAML triggers, or is than an event I can utilize when the DB refilters?
Is where I would use an ObservableCollection?

Why does my MenuItem have an Icon when I have overridden the DataTemplate?

I have successfully implement a WPF menu where the top-level items are drawn as large buttons and the lower level items are drawn as standard menu items (see my previous questions here and here).
In my original attempt at this my lower-level item template (SubItemTemplate in the example below) contained an image and a textblock. The result was something that looked like a normal menu item with an empty Icon area and the image next to the text in the text part of the menu item. I was not expecting to see the icon area in the visual display since I thought that the entire visual display would be determined by the contents of my template. The top-level template (TopLevelItemTemplate) does not have any empty icon area visible.
When I removed my image from teh lower-level template and replaced it with a style-setter for the Icon property, I got the display that I wanted.
I do not understand how and why the Icon property exists on my lower-level item DataTemplate.
Here's my code. The property HasParent is used to distinguish menu items that are not top-level (that is, the ones that are drawn with the SubItemTemplate). The section I don't understand is the DataTrigger.
Why is there an Icon property available inside that trigger?
<UserControl.Resources>
<Image x:Key="MenuIconResource16" Height="16" Width="16" Source="{Binding Icon32}" x:Shared="False" />
<HierarchicalDataTemplate x:Key="TopLevelItemTemplate" ItemsSource="{Binding Children}">
<StackPanel VerticalAlignment="Bottom" Orientation="Vertical">
<Image Width="32" Height="32" VerticalAlignment="Center" Source="{Binding Icon32}" ToolTip="{Binding UserHint}" />
<TextBlock Text="{Binding Name}"/>
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate x:Key="SubItemTemplate" ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Name}"/>
</HierarchicalDataTemplate>
</UserControl.Resources>
<WrapPanel Height="Auto">
<Menu ItemsSource="{Binding DataContext.EventMenu.TopLevel, ElementName=UserControl}" ItemTemplateSelector="{StaticResource MenuItemTemplateSelector}">
<Menu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command" Value="{Binding Command}" />
<Setter Property="CommandParameter" Value="{Binding EventType}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding HasParent}" Value="true">
<Setter Property="Icon" Value="{StaticResource MenuIconResource16}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Menu.ItemContainerStyle>
</Menu>
</WrapPanel>
I thought that the entire visual display would be determined by the contents of my template.
#dkozl noted the difference between DataTemplate and Template -- that is the important distinction. A data template is a XAML fragment that the owning control uses as part of the overall control, which may or may not include other (customizable or hard-coded) visual elements, and/or other data templates. The control template is where this visual structure of the control is defined. If you set/override a control template, then your expectation of not seeing any other visual content, will hold true.
The top-level template (TopLevelItemTemplate) does not have any empty icon area visible.
The other thing to note here is that the default style for Menu defines multiple control templates for its MenuItems. These templates are applied depending on the role "TopLevelHeader", "TopLevelItem", "SubmenuHeader", and "SubmenuItem". So you will see different behavior for these different menu items. Take a look at the default styles/templates, which should be illuminating (although they are kind of complex).
Why is there an Icon property available inside that trigger?
A style trigger has the capability of modifying any dependency property of the control it is applied to. Since the style trigger in question is being applied to the MenuItem control, it can modify the Icon dependency property, which is owned by that control.

Expander with Virtualization inside WPF Datagrid

I have a performance issue in a prototype I am working on. The requirement is to build a datagrid with multiple synchronized frozen panes, supporting grouping and sorting etc... For more details about the grid I am building, see this previous question.
Now, I have a question related to Grouping and in particular Expanders. I have a GroupStyle defined by the following Xaml, and taken from this blog post.
<!--Default GroupStyle-->
<GroupStyle x:Key="gs_Default">
<GroupStyle.Panel>
<ItemsPanelTemplate>
<DataGridRowsPresenter/>
</ItemsPanelTemplate>
</GroupStyle.Panel>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Path=Name}" FontWeight="Bold" Padding="3"/>
</StackPanel>
</DataTemplate>
</GroupStyle.HeaderTemplate>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander x:Name="exp"
BorderBrush="#FFA4B97F"
BorderThickness="0,0,0,1"
IsExpanded="{Binding Path=Items[0].IsExpanded}">
<Expander.Header>
<DockPanel TextBlock.FontWeight="Bold">
<TextBlock Text="{Binding Path=Name}" Margin="5,0,5,0" />
<TextBlock Text="{Binding Path=ItemCount}"/>
</DockPanel>
</Expander.Header>
<ItemsPresenter/>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
The Expander is not virtualized and we are experiencing a performance issue when there are several hundred rows in a group.
Has anyone encoutered this before and have a fix? I am ideally looking for a Virtualizing Expander, and have seen workarounds such as this (which doesn't solve the problem).
If you're using .NET 4 then get the ItemsPresenters visibility to track/be dependent on the IsExpanded state.
WPF DataGrid Virtualization with Grouping
In .NET 4.5 there's a new attached property VirtualizingPanel.IsVirtualizingWhenGrouping.
Another alternative is to push the grouping into the ViewModel rather than making the DataGrid/CollectionView be responsible for that. See here:
http://blog.smoura.com/wpf-toolkit-datagrid-part-iv-templatecolumns-and-row-grouping/
In order to solve this, we did the following.
We used DataTemplates to present a different row view for different row viewmodels. We had a GroupRowViewModel and ItemRowViewModel. Also a parent ViewModel which had a sorted list of Group/Item viewmodels.
When the grid was instantiated, the parent ViewModel would sort all child viewmodels into the following:
Group
Item
Item
Item
Group
Item
Item
Item
When a GroupRow is clicked you want to execute some code where the parent (which contains a sorted list of group+item rows) will remove or include the items. E.g. say the second gropu was clicked, your list of rowviewmodels you bind to now becomes
Group
Item
Item
Item
Group (Collapsed)
That's it. So no magic at all, you manually remove or include the rows you want depending on what was clicked. It worked with virtualization and hundreds of thousands of rows at an acceptible speed.
Sorry I can't post any code (due to NDA) but I hope that helps you. Also - I would suggest looking at Telerik Grid as this is awesomely fast for large datasets

Error Template Layout

<ControlTemplate x:Key="DefaultErrorTemplate">
<DockPanel LastChildFill="True">
<DKMS:WarningImage Margin="10,0,0,0"
DockPanel.Dock="Right"
Notification="{Binding ElementName=MyAdorner,
Path=AdornedElement.(Validation.Errors)[0].ErrorContent}" />
<AdornedElementPlaceholder Name="MyAdorner" />
</DockPanel>
</ControlTemplate>
And in text box style:
<Setter Property="Validation.ErrorTemplate" Value="{StaticResource DefaultErrorTemplate}" />
The question is how do I force the layout update that is triggered by mouse over? It is also triggered by a MessageBox.
If this is an ErrorTemplate, then you display it by telling the binding system that the binding is invalid. Typically this is done through the IDataErrorInfo interface. But again, this assumes that you're using some sort of binding on the TextBox, typically the Text property.
Here's a blog post about the topic: http://weblogs.asp.net/marianor/archive/2009/04/17/wpf-validation-with-attributes-and-idataerrorinfo-interface-in-mvvm.aspx
It sounds, though, like you are using "event based" programming, and so I don't think you want to use Validation.ErrorTemplate, it sounds like you really want to do some custom adorners. You might want to check out this CodeProject post: http://www.codeproject.com/Articles/54472/Defining-WPF-Adorners-in-XAML on that topic.

Showing a new Window right next to a DataCell in DataGrid (WPF)

I want a scenario when a user clicks on a cell in DataGrid in WPF, I want to open NumPad next to it (This is basically for touch based input).
The NumPad, I understand is a separate window.
1) How can I know which Cell is selected 2) how can I show the NumPad next to the cell? 3) How can I find the coordinates of cell to position my NumPad? 4) How can I set the value of cell based on NumPad entry?
NumPad is a WPF User Control in the same application.
DataGrid is a .NET 4 Control.
It's a normal Windows Desktop application
This is not a trivial task and you should have some knowledge of WPF to accomplish this, but here are some ideas what you might look for:
The DataGridCell.IsSelected property tells you whether a cell is selected.
I would use a Popup to show the NumPad directly besides the cell.
If you use a Popup you do not need the coordinates, but you can specify the relative placement using the Popup.Placement property. Also see this MSDN document: Popup Placement Behavior
You could try to use a Binding from the NumPad to the user control in the DataGridCell.
Using the DataGrid.CellStyle or the DataGridColumn.CellStyle property you can specify an alternate style for all cells of the DataGrid or some specific column. Within this style, you could change the template and add a Popup which is opened only if the current cell is selected. You can easily achieve this by binding the Popup.IsOpen property to the DataGridCell.IsSelected property.
This is just an initial idea. You will still have to have a look at the provided MSDN links and also read some other stuff about WPF. Although it might take some time to learn this 'WPF way' (i.e. only XAML), it is (in my eyes) much easier than using lots of code-behind to determine the currently selected cell, positioning a control at the correct location, transferring the data from the NumPad to the cell and so on...
I really like Gehho's answer.
Doing as he suggested, besides using the Template column over styling text columns, resulted in the following XAML:
<Grid x:Name="LayoutRoot">
<DataGrid>
<DataGrid.Columns>
<DataGridTextColumn Header="R" Binding="{Binding Color.R}" />
<DataGridTextColumn Header="G" Binding="{Binding Color.G}" />
<DataGridTextColumn Header="B" Binding="{Binding Color.B}" />
<DataGridTextColumn Header="Alpha" Binding="{Binding Color.A}" />
<DataGridTemplateColumn Header="Thumb">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Border x:Name="border" Background="{Binding}">
<Popup IsOpen="{Binding IsMouseOver, ElementName=border, Mode=OneWay}"
PopupAnimation="Fade"
Placement="MousePoint">
<Border Width="200" Height="200" Background="{Binding Background , ElementName=border, Mode=OneWay}" />
</Popup>
</Border>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
<SolidColorBrush Color="Red"/>
<SolidColorBrush Color="Green"/>
<SolidColorBrush Color="Blue"/>
<SolidColorBrush Color="Yellow"/>
<SolidColorBrush Color="SteelBlue"/>
<SolidColorBrush Color="Lime"/>
<SolidColorBrush Color="Cyan"/>
</DataGrid>
</Grid>
</Window>
Hope this helps!

Resources