Binding chart LegendItem checkbox to series visibility in WPF in C# codebehind - wpf

I have multiple column series chart getting generated in C#. I am further trying to get the legend for this chart with the checkboxes. Such that the chart displays the column series for only legend items that are checked.
I need to do this in C# code behind and not in HTML.
I have the below existing code that creates the multiple dynamic column series -
foreach (KeyValuePair<int, string> item in list)
{
foreach (System.Data.DataRow dRow in dtTable.Rows)
{
<formation of listSource>
}
ColumnSeries ser = new ColumnSeries { Title = item.Value, IndependentValueBinding = new Binding("Key"), DependentValueBinding = new Binding("Value") };
ser.ItemsSource = null;
ser.ItemsSource = listSource;
ser.DataPointStyle = columnStyleBrown;
mcChart.Series.Add(ser);
i++;
}
}
And I further want to add something to -
ser.LegendItemStyle =
So I need to know how to create a legend style with the check boxes in c#.
There can be 2 ways of achieving this-
Either by modifying the existing legend to contain check boxes also (preferred)
Or to create a new legend altogether
Can anyone please help?
Thanks in advance!

Was able to resolve this -
xaml code -
<Grid Name="LayoutRoot">
<Grid.Resources>
<Style x:Key="CategoryLegendItem" TargetType="DVC:LegendItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="DVC:LegendItem">
<StackPanel Orientation="Horizontal">
<CheckBox VerticalAlignment="Center" IsChecked="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=Owner.Visibility, Mode=TwoWay, Converter={StaticResource BooleanToVisibilityConverter1}}" Margin="0,0,3,0" />
<Rectangle Width="8" Height="8" Fill="{Binding Background}" Stroke="{Binding BorderBrush}" StrokeThickness="1" Margin="0,0,3,0" />
<DV:Title VerticalAlignment="Center" Content="{TemplateBinding Content}" />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<DVC:Chart Name="mcChart" >
</DVC:Chart>
Relevant C# code for dynamic column series -
ColumnSeries ser = new ColumnSeries { Title = kvpNuclide.Value, IndependentValueBinding = new Binding("Key"), DependentValueBinding = new Binding("Value") };
ser.ItemsSource = null;
ser.ItemsSource = listRelease;
ser.DataPointStyle = columnStyleAqua;
ser.LegendItemStyle = (Style)LayoutRoot.Resources["CategoryLegendItem"];
mcChart.Series.Add(ser);

Related

Add a Control to autogenerated ColumnHeader of DataGrid in WPF getting data from SQLite database

I need to create an excel like filter for my data imported from SQLite database.
Importing and showing data from database is doing well with
private void UpdateDataGrid(SQLiteConnection con, string sql)
{
DataSet dataSet = new DataSet();
SQLiteDataAdapter dataAdapter = new SQLiteDataAdapter(sql, con);
dataAdapter.Fill(dataSet);
dgMaterials.ItemsSource = dataSet.Tables[0].DefaultView;
}
AutoGenerateColumns is true so that I get something like this:
Data are shown in left side datagrid.
In right side listbox I collect the generated column headers with
private void dg_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
lb.Items.Add(e.Column.Header.ToString());
}
As I said, I want to create excel like filter buttons in header row beside every header text like this
On XAML site I started like this
<DataGrid x:Name="dgMaterials"
IsReadOnly="True"
CanUserReorderColumns="False"
CanUserResizeColumns="False"
CanUserSortColumns="False"
DockPanel.Dock="Left" AutoGeneratingColumn="dg_AutoGeneratingColumn"
AlternatingRowBackground="LightGray">
... input a button next to every header text ...
</DataGrid>
Autocreating of buttons in Headers is working with:
private void dg_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
////string headertext = e.Column.Header.ToString();
//StackPanel sp = new StackPanel();
//TextBlock tb = new TextBlock();
//tb.Text = e.Column.Header.ToString();
//sp.Children.Add(tb);
dgMaterials.AutoGeneratingColumn += (ss, ee) =>
{
Button b = new Button() { Content = "...", Name = "btn_" + ee.PropertyName };
b.Click += HeaderFilterButtonClick;
StackPanel stackPanel = new StackPanel() { Orientation = Orientation.Horizontal };
stackPanel.Children.Add(new TextBlock() { Text = ee.PropertyName, VerticalAlignment = VerticalAlignment.Center });
//stackPanel.Children.Add(new Button() { Content = "...", Name = "btn_" + ee.PropertyName });
stackPanel.Children.Add(b);
ee.Column.Header = stackPanel;
};
lb.Items.Add(e.Column.Header.ToString());
}
thanks #mm8
last problem:
no button is created for the first column header and I don't know why:
ideas?
You could set the Header property of the column to a Panel in the AutoGeneratingColumn event handler:
dgMaterials.AutoGeneratingColumn += (ss, ee) =>
{
StackPanel stackPanel = new StackPanel() { Orientation = Orientation.Horizontal };
stackPanel.Children.Add(new TextBlock() { Text = ee.PropertyName, VerticalAlignment = VerticalAlignment.Center });
stackPanel.Children.Add(new Button() { Content = "..." });
ee.Column.Header = stackPanel;
};
Edit your ColumnHeaderStyle in xaml
<Style x:Key="DataGridColumnHeaderStyle1" TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridColumnHeader}">
<Grid>
<Themes:DataGridHeaderBorder BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" IsClickable="{TemplateBinding CanUserSort}" IsPressed="{TemplateBinding IsPressed}" IsHovered="{TemplateBinding IsMouseOver}" Padding="{TemplateBinding Padding}" SortDirection="{TemplateBinding SortDirection}" SeparatorBrush="{TemplateBinding SeparatorBrush}" SeparatorVisibility="{TemplateBinding SeparatorVisibility}">
<StackPanel Orientation="Horizontal">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
<Button Content="..." Margin="2" Click="Button_Click"/>
</StackPanel>
</Themes:DataGridHeaderBorder>
<Thumb x:Name="PART_LeftHeaderGripper" HorizontalAlignment="Left" Style="{StaticResource ColumnHeaderGripperStyle}"/>
<Thumb x:Name="PART_RightHeaderGripper" HorizontalAlignment="Right" Style="{StaticResource ColumnHeaderGripperStyle}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
...
<DataGrid ColumnHeaderStyle="{DynamicResource DataGridColumnHeaderStyle1}"/>
private void Button_Click(object sender, RoutedEventArgs e)
{
//what you want to do on the filter click
}

WPF dynamically create button by style and set control elements inside

We have a style defined as follow:
<Style x:Key="StartButtonStyle" TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Button ... Style="{StaticResource StartBtnStyle}">
<Button.Content>
<StackPanel>
<TextBlock x:Name="Line1" Text="..." FontSize="20" />
<TextBlock x:Name="Line2" Text="..." FontSize="8" />
</StackPanel>
</Button.Content>
</Button>
</ControlTemplate>
</Setter.Value>
</Setter>
We creates a button dynamically:
var button = new Button() {
Margin = new Thickness(3d,3d,3d,10d),
Style = FindResource("StartButtonStyle") as Style,
};
We want to find the "Line1" textblock inside the new button, and set the Text property:
var line1 = (TextBlock)button.FindName("Line1");
But it finds only "null" :( How should we find the textblock inside the button? Any advice is appreciated! Thanks in advance!
Wait until the Style has been applied - there is no TextBlock elment before this - to the Button and then find the TextBlock in the visual tree:
var button = new Button()
{
Margin = new Thickness(3d, 3d, 3d, 10d),
Style = FindResource("StartButtonStyle") as Style,
};
button.Loaded += (s, e) =>
{
TextBlock line1 = FindChild<TextBlock>(button, "Line1");
if(line1 != null)
{
line1.Text = "...";
}
};
The recursive FindChild<T> method is from here.

MVVM LIght impossible to bind one view on a TabItem

I have spent more than 10 hours exploring most of what could find on the MVVM pattern and binding to a TabControl.
I prefer not giving my actual code, but my problem is slightly simple :
I'm developing an application to Import/Export IDE (Informatica Data Exchange) Articles
I've created a full DLL wich contains all Model Classes (it was a demand of the client to use that class in another app), that also contains an EF entity (in the form of a stored procedure, not tables)
I'v got an IndexMainViewModel and associated view in the App.Ressources with a defined DataTemplate that binds the V and the VM, which will contains the 2 tabs.
Each of those tabs has to display 2 different views : the ExportView which is related to my ExportViewModel, and the ImportView on the same buiding style.
For info : I've created a DLL that contains all my Models and WorkClasses (including my services and I also created a DAOlayout with interfaces there, all is great there)
I apologize if it does not look clear. If you need I'll put down my code.
Any simple Idea is most welcome.
Thanks.
In the design part i create two Tab, one is Upload and another is Download.
<UserControl.Resources>
<Style TargetType="TabItem" x:Key="MainTabItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TabItem">
<DockPanel Height="45" Width="245" >
<Separator Name="RightBorder" Style="{StaticResource {x:Static ToolBar.SeparatorStyleKey}}"
Height="30" DockPanel.Dock="Right" />
<Grid Cursor="Hand" Width="245" Background="Transparent" >
<TextBlock Name="TabItemTitle" TextAlignment="Center" VerticalAlignment="Center" HorizontalAlignment="Center"
</Grid>
<ContentPresenter ContentSource="Header"/>
</DockPanel>
<ControlTemplate.Triggers>
<Trigger Property="Name" Value="UploadTab">
<Setter TargetName="TabItemTitle" Property="Text" Value="Upload"/>
</Trigger>
<Trigger Property="Name" Value="DownloadTab">
<Setter TargetName="TabItemTitle" Property="Text" Value="Download"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<Grid HorizontalAlignment="Center" Background="White">
<TabControl Name="MediaControl" SelectionChanged="TabControl_SelectionChanged" Padding="0">
<TabItem Name="UploadTab" Style="{StaticResource MainTabItem}">
<Border Name="UploadTabPanel">
</Border>
</TabItem>
<TabItem Name="DownloadTab" Style="{StaticResource MainTabItem}">
<Border x:Name="DownloadTabPanel">
</Border>
</TabItem>
</TabControl>
</Grid>
</UserControl>
After each Tab click SelectionChanged event will be fired and show your UI under the TabItem as child of a Border.In the SelectionChanged event, you have to add UI Part...
private void TabControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
try
{
string tabItem = ((sender as TabControl).SelectedItem as TabItem).Name as string;
switch (tabItem)
{
case "UploadTab":
if (UploadInstance == null)
{
UploadInstance = new UploadInstance();
}
UploadTabPanel.Child = UploadInstance;
break;
case "DownloadTab":
if (DownloadInstance == null)
{
DownloadInstance = new DownloadInstance();
}
DownloadTabPanel.Child = DownloadInstance;
break;
}
}
catch (System.Exception ex) { }
}
I ended up to the answer myself. I was confused about the view binding in the xaml code. I figured it out simply with this :
View :
<TabControl ItemsSource="{Binding Views}">
<TabControl.ItemTemplate >
<!-- header template -->
<DataTemplate>
<TextBlock Text="{Binding Header}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<!--content template-->
<DataTemplate>
<views:ExportView/>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
ViewModel :
public sealed class IndexMainViewModel : ViewModelBase
{
private ObservableCollection<TabItem> _views;
public ObservableCollection<TabItem> Views
{
get { return _views; }
set
{
_views = value;
RaisePropertyChanged(() => Views);
}
}
public IndexMainViewModel()
{
_views = new ObservableCollection<TabItem>();
_views.Add(new TabItem { Header = "Export", Content = new ExportViewModel()});
_views.Add(new TabItem { Header = "Import", Content = new ImportViewModel()});
}
}
I also created a TabItem class with a ViewModelBase object (MVVM Light class object). This observable collection of views can be displayed if you don't miss the namespace <views:ExportView/> in the xaml code.

How to create custom button from custom control?

I'm testing some codes about custom control. I had the following styles defines in Themes folder.LayerGrid.xaml.A button with an image and text. This PanelButtonStylestyle is used in layergrid.cs.
<Style TargetType="{x:Type common:LayerGrid}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<DockPanel VerticalAlignment="Stretch" HorizontalAlignment="Stretch" LastChildFill="True"
Name="PART_ParentPanel">
<DockPanel.Resources>
<Style x:Key="PanelButtonStyle" TargetType="Button">
<Setter Property="OverridesDefaultStyle" Value="True"></Setter>
<Setter Property="Focusable" Value="False"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="BorderPath" Margin="0"
BorderThickness="0" Background="{StaticResource TabItemBackgroundBrushUnselected}"
BorderBrush="{StaticResource TabItem_BorderBrush_Selected}">
<StackPanel Orientation="Horizontal">
<Image Source="/MCLF;component/Images/图像 3.png" Width="15" Height="15"
HorizontalAlignment="Center " VerticalAlignment="Center"></Image>
<TextBlock TextTrimming="CharacterEllipsis"
Text ="{TemplateBinding Name}"></TextBlock>
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</DockPanel.Resources>
<StackPanel Name="PART_BottomCntl" Orientation="Horizontal" DockPanel.Dock="Bottom" Background="AliceBlue"></StackPanel>
<StackPanel Name="PART_LeftCntl" Orientation="Horizontal" DockPanel.Dock="Left" Background="AliceBlue">
<StackPanel.LayoutTransform>
<RotateTransform Angle="90"/>
</StackPanel.LayoutTransform>
</StackPanel>
<StackPanel Name="PART_RightCntl" Orientation="Horizontal" DockPanel.Dock="Right" Background="AliceBlue">
<StackPanel.LayoutTransform>
<RotateTransform Angle="90"/>
</StackPanel.LayoutTransform>
</StackPanel>
<Grid Name="PART_MasterGrid" IsSharedSizeScope="True" Background="AliceBlue"></Grid>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
private Button AddToColumnStackPanel(Layer layer)
{
var btn = new Button
{
//Background = Brushes.Transparent,
//BorderBrush = Brushes.Transparent,
//BorderThickness = new Thickness(0),
//Height = 22,
//MinWidth = 55.0,
Padding = new Thickness(10, 0, 10, 0),
//FontWeight = FontWeights.Bold,
Style = (Style)PART_MasterGrid.FindResource("PanelButtonStyle"),
};
btn.Click += (o, e) =>...
}
And the DP Layer.Name is determined in MainWindow.xaml with
<controls:Layer Level="1" Orientation="Column" Name="Symbols" ColumnLocation="Left">
<controls:Layer.Content>
<Grid>
<Grid.DataContext>
<vm:MainViewModel/>
</Grid.DataContext>
</Grid>
</controls:Layer.Content>
Now the problem is the DP Name=Symbols is not bind correctly into the button PanelButtonStyle
I read a similar post but that example set the whole target type to the DP somecustomControlWPF Custom Control: TemplateBinding to Image
Update: The DP Name is in class Layer,which is used to define the properties of each layer's location, orientation, name, content etc... The class LayerGrid serves as the class backing the custom control.
In LayerGrid.cs we have:
public class LayerGrid : ContentControl
{
...
}
public class Layer : UIElement
{
public enum LayerOrientation
{
Row,
Column
}
public enum LayerColumnLocation
{
Left,
Right
}
public static readonly DependencyProperty LevelProperty;
public static readonly DependencyProperty ContentProperty;
public static readonly DependencyProperty OrientationProperty;
public static readonly DependencyProperty NameProperty;
public static readonly DependencyProperty ColumnLocationProperty;
public string Name
{
get { return (string)GetValue(NameProperty); }
set { SetValue(NameProperty, value); }
}
static Layer()
{
NameProperty = DependencyProperty.Register(
"Name",
typeof(string),
typeof(Layer));
...
}
}
And LayerGrid.xaml serves the purpose of laying out the general panels; Each button will be added to the corresponding stackPanel such as PART_LeftCntl by calling the function above.
<DockPanel>
<StackPanel Name="PART_BottomCntl" Orientation="Horizontal" DockPanel.Dock="Bottom" Background="AliceBlue"></StackPanel>
<StackPanel Name="PART_LeftCntl" Orientation="Horizontal" DockPanel.Dock="Left" Background="AliceBlue">
<StackPanel.LayoutTransform>
<RotateTransform Angle="90"/>
</StackPanel.LayoutTransform>
</StackPanel>
<StackPanel Name="PART_RightCntl" Orientation="Horizontal" DockPanel.Dock="Right" Background="AliceBlue">
<StackPanel.LayoutTransform>
<RotateTransform Angle="90"/>
</StackPanel.LayoutTransform>
</StackPanel>
<Grid Name="PART_MasterGrid" IsSharedSizeScope="True" Background="AliceBlue"></Grid>
</DockPanel>
And MainWindow.xaml is responsible for giving the contents inside;
Update2 Below an example of the same style is attached.Only Buttonstyle is changed.https://www.dropbox.com/sh/os34tr8zl21uj4o/AAC_segoCWzAbJMFzCKHyZnpa?dl=0
//var btn = new Button
//{
// Background = Brushes.Transparent,
// BorderBrush = Brushes.Transparent,
// BorderThickness = new Thickness(0),
// Height = 22,
// MinWidth = 65.0,
// Padding = new Thickness(10, 0, 15, 0),
// FontWeight = FontWeights.Bold,
// Style = (Style)PART_MasterGrid.FindResource("buttonStyle"),
// Content = layer.Name
//};
var btn = new Button
{
Padding = new Thickness(10, 0, 10, 0),
//FontWeight = FontWeights.Bold,
Style = (Style)PART_MasterGrid.FindResource("PanelButtonStyle")
//Content = layer.Name
};
I think you are looking for how to create the binding in code behind.
So this should work for you
Binding b = new Binding("Name");
b.Source = layer;
btn.SetBinding(Button.ContentProperty, b);
so remove Content = layer.Name & add this code before btn.Click += (o, e) =>...
give it a try and see if this is what you are looking for
EDIT
After looking at your implementation I found that the buttons are directly added to parts (StackPanel) of the layered grid template instead of layer (see below). So Relative source may not help here.
However, there are ways how you can achieve your goal in this scenario. As an easy option you can leverage Content property of Button.
So start by binding the Text property it to the template's Content property
<TextBlock TextTrimming="CharacterEllipsis" FontSize="10"
Text ="{TemplateBinding Content}"></TextBlock>
then in the code you can simply use Content = layer.Name if the Name is not supposed to change.
eg
var btn = new Button
{
Padding = new Thickness(10, 0, 10, 0),
Style = (Style)PART_MasterGrid.FindResource("PanelButtonStyle"),
Content = layer.Name
};
Or alternatively you can bind Name with Content property to reflect the changes if needed.
eg
var btn = new Button
{
Padding = new Thickness(10, 0, 10, 0),
Style = (Style)PART_MasterGrid.FindResource("PanelButtonStyle")
};
Binding b = new Binding("Name");
b.Source = layer;
btn.SetBinding(Button.ContentProperty, b);
Let me know if this helps.

When text wraps within TextBlock, ActualHeight is incorrect

I have a TextBlock with a Border around it that's inside of a Canvas that I'm using to animate it as part of a custom control. The block slides in from the bottom of the screen over the top of the image. I'm trying to use the ActualHeight of the TextBlock to determine how far to move it onto the page, but when there is so much text that it wraps to two lines, the ActualHeight returns the same size as though there was a single line.
TextBlock:
<DataTemplate DataType="{x:Type contentTypes:BusinessAdText}" x:Key="BusinessAdTextTemplate">
<Border Background="#a9a9a975"
Width="{Binding RelativeSource={RelativeSource AncestorType={x:Type Canvas}}, Path=ActualWidth}">
<TextBlock Margin="20" Text="{Binding Text}"
TextWrapping="Wrap">
</TextBlock>
</Border>
</DataTemplate>
This style is applied which has the canvas:
<Style TargetType="local:BusinessAd">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:BusinessAd">
<Border Background="Transparent">
<Canvas ClipToBounds="True">
<ContentPresenter x:Name="PART_Content"
VerticalAlignment="Center"
HorizontalAlignment="Center" />
</Canvas>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Code behind for BusinessAd.cs has:
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_contentPart = GetTemplateChild("PART_Content") as FrameworkElement;
}
Then just using a simple DoubleAnimation I move it onto the screen:
if (_contentPart != null && _isLoaded)
{
_storyboard.Stop();
vAnimation.From = ActualHeight;
vAnimation.To = ActualHeight - _contentPart.ActualHeight;
//_contentPart.ActualHeight returns 46.something no matter how much text is there
vAnimation.Duration = new Duration(TimeSpan.FromSeconds(Duration));
if (_storyboard.Children.Count == 0)
{
_storyboard.Children.Add(vAnimation);
Storyboard.SetTargetProperty(vAnimation, new PropertyPath("(Canvas.Top)"));
Storyboard.SetTarget(vAnimation, _contentPart);
}
_storyboard.Begin();
}
You have to call UpdateLayout() before checking ActualHeight:
if (_contentPart != null && _isLoaded)
{
_storyboard.Stop();
UpdateLayout();
vAnimation.From = ActualHeight;
vAnimation.To = ActualHeight - _contentPart.ActualHeight;
//_contentPart.ActualHeight returns 46.something no matter how much text is there
vAnimation.Duration = new Duration(TimeSpan.FromSeconds(Duration));
if (_storyboard.Children.Count == 0)
{
_storyboard.Children.Add(vAnimation);
Storyboard.SetTargetProperty(vAnimation, new PropertyPath("(Canvas.Top)"));
Storyboard.SetTarget(vAnimation, _contentPart);
}
_storyboard.Begin();
}
I'm not sure if this applies to you, but for me, the textblock in Windows.UI.Xaml.Controls needs to be preceded with this:
myTextBlock.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
Before, when I had just myTextBlock.Measure(new Size());, it worked when there wasn't any text wrapping, but with wrapping ActualWidth and ActualHeight returned the dimensions of the word/letter, depending on WrapWholeWords or Wrap

Resources