I have DataGrid with ObservableCollection of My class type as DataSource. I'm using a few TemplateColumns with the same DataTemplate so i got an idea that I create class for that column.
The problem is that I don't know how to Bind data in that case.
My class obviously extends DataGridTemplateColumn
public class WindowedColumn : DataGridTemplateColumn
Also i have properties
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(Binding), typeof(WindowedColumn), new PropertyMetadata(null));
public static readonly DependencyProperty MaxLengthProperty =
DependencyProperty.Register("MaxLength", typeof(Binding), typeof(WindowedColumn), new PropertyMetadata(null));
public Binding Text
{
get { return (Binding)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public Binding MaxLength
{
get { return (Binding)GetValue(MaxLengthProperty); }
set { SetValue(MaxLengthProperty, value); }
}
My constructor code:
BindingOperations.SetBinding(this, FrameworkElement.DataContextProperty, new Binding
{
RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(DataGrid), 1)
});
var standardTemplate = new DataTemplate();
var standardTextBlock = new FrameworkElementFactory(typeof(TextBlock));
standardTextBlock.SetBinding(TextBlock.TextProperty, Text);
standardTemplate.VisualTree = standardTextBlock;
DataTemplate editingTemplate = new DataTemplate();
var editingStackPanel = new FrameworkElementFactory(typeof(StackPanel));
editingStackPanel.SetValue(StackPanel.OrientationProperty, Orientation.Horizontal);
var editingTextBox = new FrameworkElementFactory(typeof(TextBox));
editingTextBox.SetBinding(TextBox.TextProperty, Text);
//editingTextBox.SetBinding(TextBox.MaxLengthProperty, MaxLength);
editingTextBox.SetValue(TextBox.MinWidthProperty, 50.0);
editingTextBox.SetValue(TextBox.MaxWidthProperty, 200.0);
var editingButton = new FrameworkElementFactory(typeof(Button));
editingButton.SetValue(ContentControl.ContentProperty, ":");
editingStackPanel.AppendChild(editingTextBox);
editingStackPanel.AppendChild(editingButton);
editingTemplate.VisualTree = editingStackPanel;
CellTemplate = standardTemplate;
CellEditingTemplate = editingTemplate;
in my xaml file i use it like that
<Columns:WindowedColumn Header="My Column" Text="{Binding OrderNumber}" MaxLength="{Binding MyMaxLengthProperty}"/>
Visual shows me in xaml that no dataContext found for OrderNumber and of course when I run the program I don't see any value which i see in other TextColumn with the same binding for test
I found the solution. If anyone ever want to create custom column and use ItemsSource for DataGrid should know that data are store in rows not in DataGrid so you can't relate to DataContext at DataGrid. You should relate to DataContext in DataGridRow. Here is example how it can works:
Text="{Binding DataContext.OrderNumber, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGridRow}}}"
You should also create columns after property change event like that
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
if (e.Property == TextProperty || e.Property == MaxLengthProperty)
{
CellTemplate.VisualTree = CreateContentTemplate();
CellEditingTemplate.VisualTree = CreateEditingTemplate();
}
}
Constructor can be simplified to
public WindowedColumn()
{
CellTemplate = new DataTemplate();
CellEditingTemplate = new DataTemplate();
}
Your mistake is in the type of properties and in expectation of RelativeSource binding. The DataGridColumn is not a UI element and is not a Freezable. Because of this, it "does not see" the current Data Context and is not included in the visual tree. Therefore, setting a binding of type RelativeSource FindAncestor in it will not work.
Your Text and MaxLength properties should be regular CLR properties. It's better to name them TextBinding and MaxLengthBinding.
Implementation example:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace Core2023.SO.hubert_kwiecień
{
public class WindowedColumn : DataGridTemplateColumn
{
private BindingBase? _maxLength;
private BindingBase? _text;
public BindingBase? TextBinding { get => _text; set => Set(ref _text, value); }
public BindingBase? MaxLengthBinding { get => _maxLength; set => Set(ref _maxLength, value); }
private void Set(ref BindingBase? _field, BindingBase? #new)
{
if (BindingBase.Equals(_field, #new))
return;
_field = #new;
BindingChanged();
}
private void BindingChanged()
{
var standardTemplate = new DataTemplate();
var standardTextBlock = new FrameworkElementFactory(typeof(TextBlock));
if (TextBinding is not null)
standardTextBlock.SetBinding(TextBlock.TextProperty, TextBinding);
standardTemplate.VisualTree = standardTextBlock;
DataTemplate editingTemplate = new DataTemplate();
var editingStackPanel = new FrameworkElementFactory(typeof(StackPanel));
editingStackPanel.SetValue(StackPanel.OrientationProperty, Orientation.Horizontal);
var editingTextBox = new FrameworkElementFactory(typeof(TextBox));
if (TextBinding is not null)
editingTextBox.SetBinding(TextBox.TextProperty, TextBinding);
if (MaxLengthBinding is not null)
editingTextBox.SetBinding(TextBox.MaxLengthProperty, MaxLengthBinding);
editingTextBox.SetValue(TextBox.MinWidthProperty, 50.0);
editingTextBox.SetValue(TextBox.MaxWidthProperty, 200.0);
var editingButton = new FrameworkElementFactory(typeof(Button));
editingButton.SetValue(ContentControl.ContentProperty, ":");
editingStackPanel.AppendChild(editingTextBox);
editingStackPanel.AppendChild(editingButton);
editingTemplate.VisualTree = editingStackPanel;
CellTemplate = standardTemplate;
CellEditingTemplate = editingTemplate;
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
namespace Core2023.SO.hubert_kwiecień
{
public class SomeExampleOrderNumber
{
public int OrderNumber { get; set; }
private static readonly Random random = new Random();
public static IEnumerable<SomeExampleOrderNumber> Items { get; }
= Enumerable
.Range(1, 10)
.Select(_ => new SomeExampleOrderNumber() { OrderNumber = random.Next()})
.ToArray();
}
}
<Window x:Class="Core2023.SO.hubert_kwiecień.WindowedColumnWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Core2023.SO.hubert_kwiecień"
mc:Ignorable="d"
Title="WindowedColumnWindow" Height="450" Width="800">
<Grid>
<DataGrid ItemsSource="{x:Static local:SomeExampleOrderNumber.Items}">
<DataGrid.Columns>
<local:WindowedColumn Header="My Column"
TextBinding="{Binding OrderNumber}"
MaxLengthBinding="{Binding Source=20}"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
Related
Xaml as below:
<ItemsControl
x:Class="PowersOf2.Windows10.Views.Controls.Board"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:PowersOf2.Windows10.Views.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Name="Root" ItemsSource="{Binding Fields, ElementName=Root}" Loaded="Root_Loaded"
>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid
Width="{Binding FieldWidth, ElementName=Root}"
Height="{Binding FieldHeight, ElementName=Root}"
Loaded="Grid_Loaded" Background="White"
>
<Grid.RenderTransform>
<TranslateTransform X="{Binding X}" Y="{Binding Y}"/>
</Grid.RenderTransform>
<TextBlock Text="{Binding Text}" Foreground="Black"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Fields is IEnumerable of Field which has coordinates X and Y. They are managed by view model. FieldWidth and FieldHeight are dependency properties calculated in code behind.
How to get binding object of nested dependency properties such as TranslateTransform.X and TranslateTransform.Y in code behind?
UPDATE:
Based on this question: Fredrik's answer works as expected until you work with single embedded object in xaml with binding to non-nested properties, but not for nested ones. This issue is more complicated due to ItemsControl containing my Grid.
Code behind below:
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using Windows.UI.Xaml;
namespace Controls
{
public sealed partial class Board
{
public Board()
{
InitializeComponent();
}
private void Root_Loaded(object sender, RoutedEventArgs e)
{
FieldWidth = 100.0;
FieldHeight = 100.0;
Fields =
new Field[]
{
new Field { X = 100, Y = 100, Text = "one" },
new Field { X = 300, Y = 300, Text = "two" }
};
}
public double FieldWidth
{
get { return (double)GetValue(FieldWidthProperty); }
set { SetValue(FieldWidthProperty, value); }
}
public static readonly DependencyProperty FieldWidthProperty = DependencyProperty.Register(
"FieldWidth", typeof(double), typeof(Board), new PropertyMetadata(0.0)
);
public double FieldHeight
{
get { return (double)GetValue(FieldHeightProperty); }
set { SetValue(FieldHeightProperty, value); }
}
public static readonly DependencyProperty FieldHeightProperty = DependencyProperty.Register(
"FieldHeight", typeof(double), typeof(Board), new PropertyMetadata(0.0)
);
public IEnumerable<Field> Fields
{
get { return (ObservableCollection<Field>)GetValue(FieldsProperty); }
set { SetValue(FieldsProperty, value); }
}
public static readonly DependencyProperty FieldsProperty = DependencyProperty.Register(
"Fields", typeof(IEnumerable<Field>), typeof(Board), new PropertyMetadata(null)
);
private void Grid_Loaded(object sender, RoutedEventArgs e)
{
// here I want to get binding of RenderTransform's properties
}
}
public class Field : INotifyPropertyChanged
{
private int _x;
public int X
{
get { return _x; }
set
{
_x = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("X"));
}
}
private int _y;
public int Y
{
get { return _y; }
set
{
_y = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Y"));
}
}
private string _text;
public string Text
{
get { return _text; }
set
{
_text = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Text"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
I hope I haven't misinterpreted the question but you can get the transformation and the bound item like this.
private void Grid_Loaded(object sender, RoutedEventArgs e)
{
var grid = (Grid)sender;
//the actual transformation
var render = (Transform)grid.GetValue(RenderTransformProperty);
//the field the transformation is bound to
var field = (Field)grid.DataContext;
//for now this only works in WPF
var binding = BindingOperations.GetBinding(render, TranslateTransform.XProperty);
}
Made an edit for this, but it does not work for winrt.
The method BindingOperations.GetBinding is only available in WPF.
Hope that winrt gets this soon.
I have a DataGrid backed by an Observable collection of Dictionary. I'd like to perform edits via the grid and currently the UI doesn't seem to allow it. I also observe that when adding the IEditableObject interface to the Dictionary, the interface methods are called when a cell is clicked, but nothing happens on the UI itself.
Here's my minimum example:
class MainWindowViewModel
{
public MainWindowViewModel() {
ItemsSource = new ObservableCollection<DataGridData> {
new DataGridData() {{"Name", "Abe"},{"Age", "50"},{"Gender", "Male"}},
new DataGridData() {{"Name", "Shelly"},{"Age", "20"},{"Gender", "Female"}
}
};
}
public ObservableCollection<DataGridData> ItemsSource { get; set; }
}
public class DataGridData : Dictionary<string, string>, IEditableObject {
public void BeginEdit(){}
public void CancelEdit(){}
public void EndEdit(){}
}
public partial class MainWindow {
public MainWindow() {
DataContext = new MainWindowViewModel();
InitializeComponent();
foreach (var col in ViewModel.ItemsSource[0].Keys) {
AddColumns(col, col);
}
}
MainWindowViewModel ViewModel {
get { return DataContext as MainWindowViewModel; }
}
void AddColumns(string id, string name)
{
FrameworkElementFactory textBlock = new FrameworkElementFactory(typeof(TextBlock));
textBlock.SetValue(TextBlock.PaddingProperty, new Thickness(2));
textBlock.SetValue(TextBlock.TextProperty, new Binding(string.Format("[{0}]", id)));
Binding textDecorationBinding = new Binding();
textDecorationBinding.ElementName = "DataGrid";
textDecorationBinding.Path = new PropertyPath("DataContext.TextDecoration");
textBlock.SetValue(TextBlock.TextDecorationsProperty, textDecorationBinding);
DataTemplate cellTemplate = new DataTemplate();
cellTemplate.VisualTree = textBlock;
DataGridTemplateColumn column = new DataGridTemplateColumn();
column.Header = name;
column.SortMemberPath = name;
column.CellTemplate = cellTemplate;
DataGrid.Columns.Add(column);
}
}
<Window x:Class="Datagrid.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<DataGrid
Name="DataGrid"
ItemsSource="{Binding ItemsSource}"
AutoGenerateColumns="False"
>
</DataGrid>
</Grid>
</Window>
The dictionary is used because I don't anything about the column data at compile time. The ItemsSource above is just an example but I could have any number of key/values at run time.
What changes are necessary to enable editing the cell data?
Edit
I've never used it, but I'm reading about using reflection to emit an actual class that could replace the dictionary key/values. I may give this a go unless someone has contrary advice.
In my opition your code is a little bit "tortuous". Indeed I wouldn't use a Dictionary as model of the objects which I want to handle. In my opinion it is better to use a specialized class (in this way you can implement the "famous" INotifyPropertyChanged interface).
For example you can use a Person class (here a very fast implementation of it):
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public Genders Gender { get; set; }
}
You can implement IEditableObject, but it is not mandatory.
Last (but not the least) if I were you, I will declare DataGrid columns in my XAML and not in my code (I can't see a valid reason for using code in your situation; I don't know maybe you need to have dynamic columns. In this case you can read this very good article).
So my ViewModel is (you can easy replace Dictionary with Person class):
public class MainWindowViewModel
{
public MainWindowViewModel()
{
People = new ObservableCollection<Dictionary<string, string>> {
new Dictionary<string, string>() {{"Name", "Abe"}, {"Age", "50"}, {"Gender", "Male"}},
new Dictionary<string, string>() {{"Name", "Shelly"}, {"Age", "20"}, {"Gender", "Female"}}
};
}
public ObservableCollection<Dictionary<string, string>> People { get; private set; }
}
My XAML (if you replaced Dictionary with Person, remove the square brackets from the columns bidings):
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<StackPanel Orientation="Vertical">
<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Path=People}">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Path=[Name]}" Header="Name" Width="2*" />
<DataGridTextColumn Binding="{Binding Path=[Age]}" Header="Age" Width="*" />
<DataGridTextColumn Binding="{Binding Path=[Gender]}" Header="Gender" Width="*" />
</DataGrid.Columns>
</DataGrid>
</StackPanel>
</Window>
My window code-behind:
namespace WpfApplication1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel();
}
}
}
In this way, everything works fine and you can edit data by using the DataGrid.
I hope this sample can help you.
EDIT
If you need to use your approach, probably in your AddColumns method you are missing to declare a CellEditingTemplate. Your method will become:
private void AddColumns(string id, string name)
{
FrameworkElementFactory textBlock = new FrameworkElementFactory(typeof(TextBlock));
textBlock.SetValue(TextBlock.PaddingProperty, new Thickness(2));
textBlock.SetValue(TextBlock.TextProperty, new Binding(String.Format("[{0}]", id)));
FrameworkElementFactory textBox = new FrameworkElementFactory(typeof(TextBox));
textBox.SetValue(TextBox.PaddingProperty, new Thickness(2));
textBox.SetValue(TextBox.TextProperty, new Binding(String.Format("[{0}]", id)));
Binding textDecorationBinding = new Binding();
textDecorationBinding.ElementName = "DataGrid";
textDecorationBinding.Path = new PropertyPath("DataContext.TextDecoration");
textBlock.SetValue(TextBlock.TextDecorationsProperty, textDecorationBinding);
DataTemplate cellTemplate = new DataTemplate();
cellTemplate.VisualTree = textBlock;
DataTemplate cellEditingTemplate = new DataTemplate();
cellEditingTemplate.VisualTree = textBox;
DataGridTemplateColumn column = new DataGridTemplateColumn();
column.Header = name;
column.SortMemberPath = name;
column.CellTemplate = cellTemplate;
column.CellEditingTemplate = cellEditingTemplate;
DataGrid.Columns.Add(column);
}
I read that you are thinking to use reflection to emit an actual class that could replace the dictionary key/values. Of course this is a solution, but I suggest to consider ICustomTypeDescriptor or CustomTypeDescriptor.
I have tried all morning to get this to work with no luck. I am using DynamicDataDisplay (D3) to display a graph. Here is my simple view defined using xaml.
<Window x:Class="BMSVM_Simulator.View.GraphWindowView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ViewModel="clr-namespace:BMSVM_Simulator.ViewModel"
xmlns:d3="http://research.microsoft.com/DynamicDataDisplay/1.0"
x:Name="ThisGraphWindowInstance"
Title="Plot" Height="500" Width="750"
WindowStartupLocation="CenterOwner"
Icon="../res/qualcomm_q_icon.ico.ico"
MinWidth="400" MinHeight="300">
<Window.DataContext>
<ViewModel:GraphWindowPresenter/>
</Window.DataContext>
<Grid>
<d3:ChartPlotter Name="plotter" Margin="10,10,20,10">
<d3:InjectedPlotter Name="innerPlotter" Background="Aqua" SetViewportBinding="False">
<d3:VerticalAxis Placement="Right"/>
<d3:VerticalAxisTitle Content="{Binding ElementName=ThisGraphWindowInstance, Path=yAxis2}" Placement="Right"/>
</d3:InjectedPlotter>
<d3:Header FontFamily="Arial" Content="{Binding ElementName=ThisGraphWindowInstance, Path=title}"/>
<d3:VerticalAxisTitle FontFamily="Arial" Content="{Binding ElementName=ThisGraphWindowInstance, Path=yAxis2}"/>
<d3:HorizontalAxisTitle FontFamily="Arial" Content="{Binding ElementName=ThisGraphWindowInstance, Path=title}"/>
</d3:ChartPlotter>
</Grid>
</Window>
The issue is that the:
<d3:VerticalAxisTitle Content="{Binding ElementName=ThisGraphWindowInstance, Path=yAxis2}" Placement="Right"/>
in the InjectedPlotter does not display at all when I use the current setup with the Content bound to Path=yAxis2. I set at breakpoint and I see that yAxis2 is actually a defined string and that it is not null.
When I actually hardcode a value such that Content="DEFAULT TITLE", so it then becomes :
<d3:VerticalAxisTitle Content="DEFAULT TITLE" Placement="Right"/>
the title displays fine.
Does anyone know why this is happening?
Here is the code behind for reference:
public static readonly DependencyProperty yAxis2Property =
DependencyProperty.Register("yAxis2", typeof(string), typeof(GraphWindowView));
public string yAxis2
{
get { return (string)GetValue(yAxis2Property); }
set { SetValue(yAxis2Property, value); }
}
public void ShowGraph()
{
// consume ChartData
this.yAxis1 = ChartData.yAxisTitle1;
this.yAxis2 = "AXIS 2 TITLE..SHOW UP!";
.....
}
EDIT >>>>>>>>>
using BMSVM_Simulator.ViewModel;
using Microsoft.Research.DynamicDataDisplay;
using Microsoft.Research.DynamicDataDisplay.DataSources;
using Microsoft.Research.DynamicDataDisplay.Navigation;
using Microsoft.Research.DynamicDataDisplay.PointMarkers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace BMSVM_Simulator.View
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class GraphWindowView : Window
{
#region Fields
private readonly int DEFAULT_AXIS_WIDTH = 20;
private readonly Pen[] colors = {
new Pen(Brushes.Blue, 2),
new Pen(Brushes.DarkGreen, 2),
new Pen(Brushes.DarkMagenta, 2),
new Pen(Brushes.DarkSalmon, 2),
new Pen(Brushes.Maroon, 2),
new Pen(Brushes.Orange, 2),
new Pen(Brushes.SkyBlue, 2)
};
#endregion
#region DependencyProperties
public static readonly DependencyProperty yAxis1Property =
DependencyProperty.Register("yAxis1", typeof(string), typeof(GraphWindowView));
public static readonly DependencyProperty yAxis2Property =
DependencyProperty.Register("yAxis2", typeof(string), typeof(GraphWindowView));
public static readonly DependencyProperty titleProperty =
DependencyProperty.Register("title", typeof(string), typeof(GraphWindowView));
public static readonly DependencyProperty xAxisProperty =
DependencyProperty.Register("xAxis", typeof(string), typeof(GraphWindowView));
public static readonly DependencyProperty DatesProperty =
DependencyProperty.Register("Dates", typeof(EnumerableDataSource<int>), typeof(GraphWindowView));
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register("Data", typeof(EnumerableDataSource<int>), typeof(GraphWindowView));
public static readonly DependencyProperty ChartDataProperty =
DependencyProperty.Register("ChartData", typeof(ChartData), typeof(GraphWindowView));
public static readonly DependencyProperty rightAxisWidthProperty =
DependencyProperty.Register("rightAxisWidth", typeof(int), typeof(GraphWindowView));
public int rightAxisWidth
{
get { return (int)GetValue(rightAxisWidthProperty); }
set { SetValue(rightAxisWidthProperty, value); }
}
public ChartData ChartData
{
get { return (ChartData)GetValue(ChartDataProperty); }
set { SetValue(ChartDataProperty, value); }
}
public EnumerableDataSource<int> Dates
{
get { return (EnumerableDataSource<int>)GetValue(DatesProperty); }
set { SetValue(DatesProperty, value); }
}
public EnumerableDataSource<int> Data
{
get { return (EnumerableDataSource<int>)GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
public string xAxis
{
get { return (string)GetValue(xAxisProperty); }
set { SetValue(xAxisProperty, value); }
}
public string yAxis1
{
get { return (string)GetValue(yAxis1Property); }
set { SetValue(yAxis1Property, value); }
}
public string title
{
get { return (string)GetValue(titleProperty); }
set { SetValue(titleProperty, value); }
}
public string yAxis2
{
get { return (string)GetValue(yAxis2Property); }
set { SetValue(yAxis2Property, value); }
}
#endregion
public GraphWindowView()
{
InitializeComponent();
rightAxisWidth = DEFAULT_AXIS_WIDTH;
}
public void ShowGraph()
{
// consume ChartData
this.xAxis = ChartData.xAxisTitle;
this.yAxis1 = ChartData.yAxisTitle1;
this.yAxis2 = "AXIS 2 TITLE..SHOW UP!"; // ChartData.yAxisTitle2;
this.title = ChartData.title;
this.rightAxisWidth = DEFAULT_AXIS_WIDTH;
// list of data points
List<DataSet> dataSets = this.ChartData.dataPoints;
int colorCounter = 0;
int rightAxisCount = 0;
foreach (DataSet set in dataSets)
{
set.dates.SetXMapping(x => x);
set.data.SetYMapping(x => x);
CompositeDataSource compositeDataSource1 = new
CompositeDataSource(set.dates, set.data);
if (set.axis == AxisSide.LEFT)
{
plotter.AddLineGraph(compositeDataSource1, colors[colorCounter % colors.Length],
new CirclePointMarker { Size = 8.00, Fill = Brushes.Red },
new PenDescription(set.legendTitle));
}
else
{
innerPlotter.AddLineGraph(compositeDataSource1, colors[colorCounter % colors.Length],
new CirclePointMarker { Size = 8.00, Fill = Brushes.Red },
new PenDescription(set.legendTitle));
rightAxisCount++;
}
colorCounter++;
}
// if there is nothing plotted against the right axis, don't show it
if (rightAxisCount == 0)
{
rightAxisWidth = 0;
}
plotter.Viewport.FitToView();
// there are duplicate legends, so we hide one
plotter.LegendVisibility = Visibility.Hidden;
Show();
}
}
}
In the code you provided, you have set the datacontext as an object of GraphWindowPresenter, but while declaring the dependancy property you have set the GraphWindowView object. Please make sure that you set the appropriate object as datacontext
<Window.DataContext>
< ViewModel:GraphWindowPresenter/>
< /Window.DataContext>
DependencyProperty.Register("yAxis2", typeof(string), typeof(GraphWindowView))
Try :
<d3:VerticalAxisTitle Content="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=yAxis2,Mode=OneWay}" />
I ran a quick test binding a TextBox.Text, and the code you have posted works.
<Window x:Class="WpfApplication2.MainWindow"
x:Name="TestWindow" ...>
<StackPanel>
<!-- both bindings work -->
<TextBlock Text="{Binding ElementName=TestWindow, Path=yAxis2}" />
<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:MainWindow}}, Path=yAxis2}" />
</StackPanel>
</Window>
public partial class MainWindow : Window
{
public static readonly DependencyProperty yAxis2Property =
DependencyProperty.Register("yAxis2", typeof(string), typeof(MainWindow));
public string yAxis2
{
get { return (string)GetValue(yAxis2Property); }
set { SetValue(yAxis2Property, value); }
}
public MainWindow()
{
InitializeComponent();
this.yAxis2 = "TESTING";
}
}
So my best guess is that either
you are not calling ShowGraph() on the window
or the VerticalAxisTitle object is not one that exists in the Visual Tree, much like some other WPF objects like DataGridColumn
To determine if the first issue is your problem, simply ensure you are calling ShowGraph() in the constructor behind your window, or just set yAxis2 the way I have here for testing.
You could also use a tool like Snoop that is very useful for debugging runtime databindings.
If that is done and it's still not showing up correctly, then you may need to do some more research into the VerticalAxisTitle to find workarounds on how to bind it correctly. If you have trouble finding anything specific to VerticalAxisTitle, try looking up how it's done for DataGridColumn, such as this answer.
(As a side note, it's a standard convention to capitalize public properties, so your property should be YAxis2. Just my OCD kicking in.) :)
I'm using mvvm -
I have been Googling this for a few hours, and I can't seem to find an example of how to set the focus to a cell and place it in edit mode.
If I have a datagrid and I want set focus in a determinate cell from viewmodel.
how can I do?
From your general question I think this might solve the problem:
grid.Focus();
grid.CurrentCell = new DataGridCellInfo(grid.Items[rowIndex],grid.Columns[columnIndex]);
Edit
for MVVM pattern it would be like this:
public MainWindow()
{
InitializeComponent();
DataContext = this;
for (int i = 0; i < 10; i++)
{
Items.Add(new VM() { Text1=i.ToString(), Text2 = (i+100).ToString()});
}
FocusCommand = new MyCommand(o =>
{
var dg = o as DataGrid;
if (dg != null) {
dg.Focus();
FocusedCell = new DataGridCellInfo(
dg.Items[FocusedRowIndex], dg.Columns[FocusedColumnIndex]);
}
});
}
//Items Observable Collection
public ObservableCollection<VM> Items { get { return _myProperty; } }
private ObservableCollection<VM> _myProperty = new ObservableCollection<VM>();
//FocusedCell Dependency Property
public DataGridCellInfo FocusedCell
{
get { return (DataGridCellInfo)GetValue(FocusedCellProperty); }
set { SetValue(FocusedCellProperty, value); }
}
public static readonly DependencyProperty FocusedCellProperty =
DependencyProperty.Register("FocusedCell", typeof(DataGridCellInfo), typeof(MainWindow), new UIPropertyMetadata(null));
//FocusCommand Dependency Property
public MyCommand FocusCommand
{
get { return (MyCommand)GetValue(FocusCommandProperty); }
set { SetValue(FocusCommandProperty, value); }
}
public static readonly DependencyProperty FocusCommandProperty =
DependencyProperty.Register("FocusCommand", typeof(MyCommand), typeof(MainWindow), new UIPropertyMetadata(null));
//FocusedRowIndex Dependency Property
public int FocusedRowIndex
{
get { return (int)GetValue(FocusedRowIndexProperty); }
set { SetValue(FocusedRowIndexProperty, value); }
}
public static readonly DependencyProperty FocusedRowIndexProperty =
DependencyProperty.Register("FocusedRowIndex", typeof(int), typeof(MainWindow), new UIPropertyMetadata(0));
//FocusedColumnIndex Dependency Property
public int FocusedColumnIndex
{
get { return (int)GetValue(FocusedColumnIndexProperty); }
set { SetValue(FocusedColumnIndexProperty, value); }
}
public static readonly DependencyProperty FocusedColumnIndexProperty =
DependencyProperty.Register("FocusedColumnIndex", typeof(int), typeof(MainWindow), new UIPropertyMetadata(0));
XAML:
<StackPanel Width="100">
<DataGrid ItemsSource="{Binding Items}" AutoGenerateColumns="False"
x:Name="datagrid"
CurrentCell="{Binding FocusedCell}">
<DataGrid.Columns>
<DataGridTextColumn Header="col1" Binding="{Binding Text1}"/>
<DataGridTextColumn Header="col2" Binding="{Binding Text2}"/>
</DataGrid.Columns>
</DataGrid>
<TextBox Text="{Binding FocusedRowIndex}" Margin="10"/>
<TextBox Text="{Binding FocusedColumnIndex}" Margin="10"/>
<Button Command="{Binding FocusCommand}"
CommandParameter="{Binding ElementName=datagrid}" Content="focus"/>
</StackPanel>
Now if you type the desired row index in first textbox and the desired column index in second one, and then click on focus button, it should focus on a cell.
to make sure it's working, after clicking focus, start typing something.
I'm trying to add a new TabItem to TabControl each time I click on a button and I have no problem with that. But I want a textbox inside each TabItem. How do I do that? I need to do that with code I suppose.
TabItem newTab = new TabItem();
newTab.Header = ncn.courseName;
newTab.FontSize = 20;
TextBox textbox = new TextBox();
textbox.Width = 200;
textbox.Height = 100;
textbox.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
textbox.VerticalAlignment = System.Windows.VerticalAlignment.Top;
Grid grid = new Grid();
grid.Children.Add(textbox);
newTab.Content = grid;
this.Courses.Items.Add(newTab);
this.Courses.SelectedItem = newTab;
If you would like to use only code and not the MVVM pattern, this can be solved this way:
private void button1_Click(object sender, RoutedEventArgs e)
{
TabItem item = null;
Grid grid = null;
TextBox textbox = null;
try
{
// Creating the TextBox
textbox = new TextBox();
textbox.Width = 200;
textbox.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
textbox.VerticalAlignment = System.Windows.VerticalAlignment.Top;
// Creating the Grid (create Canvas or StackPanel or other panel here)
grid = new Grid();
grid.Children.Add(textbox); // Add more controls
item = new TabItem();
item.Header = "Hello, this is the new tab item!";
item.Content = grid; // OR : Add a UserControl containing all controls you like, OR use a ContentTemplate
MyTabControl.Items.Add(item);
MyTabControl.SelectedItem = item; // Setting focus to the new TabItem
}
catch (Exception ex)
{
MessageBox.Show("Error creating the TabItem content! " + ex.Message);
}
finally
{
textbox = null;
grid = null;
item = null;
}
}
That is solving it "the old way" by using code-behind.
If you on the other want to use the WPF like it should, you can do like this.
To simplify a bit, I am using the code-behind as DataContext. I would recommend using a class instead in the running code.
I have also used the Cutton click event instead if using the Button Command.
First I create a "holder" class for the tab items, holding the data you need.
TabItemHolder.cs
public class TabItemHolder : DependencyObject, INotifyPropertyChanged
{
public static readonly DependencyProperty HeaderProperty = DependencyProperty.Register("Header", typeof(String), typeof(TabItemHolder), new UIPropertyMetadata());
public String Header
{
get { return (String)GetValue(HeaderProperty); }
set
{
SetValue(HeaderProperty, value);
NotifyPropertyChanged("Header");
}
}
public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(String), typeof(TabItemHolder), new UIPropertyMetadata());
public String Text
{
get { return (String)GetValue(TextProperty); }
set
{
SetValue(TextProperty, value);
NotifyPropertyChanged("Text");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(String PropertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
}
}
Then I have the model class, in this example the MainWindow.cs itself:
MainWindow.cs
public partial class MainWindow : Window, INotifyPropertyChanged
{
public static readonly DependencyProperty SelectedTabProperty = DependencyProperty.Register("SelectedTab", typeof(TabItemHolder), typeof(MainWindow), new UIPropertyMetadata());
public TabItemHolder SelectedTab
{
get { return (TabItemHolder)GetValue(SelectedTabProperty); }
set
{
SetValue(SelectedTabProperty, value);
NotifyPropertyChanged("SelectedTab");
}
}
public static readonly DependencyProperty TabsProperty = DependencyProperty.Register("Tabs", typeof(ObservableCollection<TabItemHolder>), typeof(MainWindow), new UIPropertyMetadata());
public ObservableCollection<TabItemHolder> Tabs
{
get { return (ObservableCollection<TabItemHolder>)GetValue(TabsProperty); }
set
{
SetValue(TabsProperty, value);
NotifyPropertyChanged("Tabs");
}
}
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
this.Tabs = new ObservableCollection<TabItemHolder>();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
this.Tabs.Add(new TabItemHolder() { Header = "Hello, this is the new tab item!", Text = "Dummy text for the textbox" });
this.SelectedTab = this.Tabs[this.Tabs.Count - 1];
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(String PropertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
}
}
And finally, the XAML would be something like this.
MainWindow.xaml
<Grid x:Name="LayoutRoot">
<TabControl x:Name="MyTabControl"
Margin="12,67,12,12"
ItemsSource="{Binding Tabs}"
SelectedItem="{Binding SelectedTab}">
<TabControl.ContentTemplate>
<DataTemplate>
<Grid>
<TextBox Text="{Binding Path=Text}"
Width="200"
HorizontalAlignment="Left"
VerticalAlignment="Top" />
</Grid>
</DataTemplate>
</TabControl.ContentTemplate>
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Header}"/>
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
<Button Content="Button" Height="34" HorizontalAlignment="Left" Margin="19,12,0,0" Name="button1" VerticalAlignment="Top" Width="90" Click="button1_Click" />
</Grid>
That would do the same trick in a different (and in my opinion better) way.
I hope that helps you.