I am using AvalonDock and MEF plugin architecture,
Each plugin returns a data template to host, host get the data template, insert to main data template.
Following are user controls that are converted to DataTemplates
MainMethodView: including a tab,
PluginA's MethodView: need to be inserted to MainMethodView's tab item 1.
PluginB's MethodView: need to be inserted to MainMethodView's tab item 2.
.....
Thanks.
Code: InitializePlugins() i have only could shows one plugin's datatemplate. and GetMethodViewTemplate() gives me error: Content of a ContentControl must be a single element.
reference: Link1
public void InitializePlugins(){
var templateSelector = new PanesTemplateSelector();
templateSelector.MethodViewTemplate = pluginService.Plugins[0].MethodViewTemplate;
_dockingManger.LayoutItemTemplateSelector = templateSelector;
}
private static DataTemplate GetMethodViewTemplate(PluginService pluginService) {
FrameworkElementFactory factory = new FrameworkElementFactory(typeof(MethodView));
foreach (var plugin in pluginService.Plugins) {
FrameworkElementFactory fef = new FrameworkElementFactory(typeof(ContentControl));
fef.SetValue(ContentControl.ContentTemplateProperty, plugin.MethodViewTemplate);
factory.AppendChild(fef);
}
DataTemplate dt = new DataTemplate();
dt.VisualTree = factory;
return dt;
}
Another problem is databinding, MainMethodViewModel has PluginMethodViewModels from plugins, How it could be binded to MainMethodView.
This is the solution I found. Share everyone.
Note: View1 and View2 are usercontrol, binding setting is in xaml file.
public MainWindow() {
InitializeComponent();
CreateTemplate4();
}
private void CreateTemplate4() {
var method = new MethodViewModel();
FrameworkElementFactory fefWrapper = new FrameworkElementFactory(typeof(TabControl));
foreach (var fred in method.Freds) {
DataTemplate dt1 = fred.Template;
FrameworkElementFactory fefTop = new FrameworkElementFactory(typeof(ContentControl));
fefTop.SetValue(ContentControl.ContentTemplateProperty, dt1);
fefTop.SetValue(ContentControl.ContentProperty, fred);
fefWrapper.AppendChild(fefTop);
}
DataTemplate dtWrapper = new DataTemplate(typeof(MethodViewModel));
dtWrapper.VisualTree = fefWrapper;
this.DataContext = method;
this.cc.ContentTemplate = dtWrapper;
}
class MethodViewModel {
public ObservableCollection<Fred> Freds { get; set; }
public MethodViewModel() {
Freds = new ObservableCollection<Fred>();
Freds.Add(new Fred1(1));
Freds.Add(new Fred2(2));
}
}
public class Fred {
public int X { get; set; }
public int y { get; set; }
public Fred(int x) {
this.X = x;
this.y = x + 1;
}
public DataTemplate Template { get; set; }
}
public class Fred1 : Fred {
public Fred1(int x) : base(x) {
FrameworkElementFactory factory = new FrameworkElementFactory(typeof(View1));
DataTemplate dt = new DataTemplate();
dt.VisualTree = factory;
this.Template = dt;
}
}
public class Fred2 : Fred {
public Fred2(int x) : base(x) {
FrameworkElementFactory factory = new FrameworkElementFactory(typeof(View2));
DataTemplate dt = new DataTemplate();
dt.VisualTree = factory;
this.Template = dt;
}
}
Related
i want to show some values in a data Grid and these values are not from database.
I am making a POS in which when user enters an item it should be shown to him/her in a data grid form.
This is what i've tried and my mistake was i didn't bind the "Name","Price" but now i've correct it and now it works perfectly
public struct MyData
{
public int Price { set; get; }
public string Name { set; get; }
}
public MainWindow()
{
InitializeComponent();
DataGridTextColumn grid_C1 = new DataGridTextColumn();
DataGridTextColumn grid_C2 = new DataGridTextColumn();
dGrid.Columns.Add(grid_C1);
dGrid.Columns.Add(grid_C2);
grid_C1.Binding = new Binding("Name");
grid_C2.Binding = new Binding("Price");
grid_C1.Header = "Name";
grid_C2.Header = "Price";
dGrid.Items.Add(new MyData { Name = "dumyText", Price = 2 });
dGrid.Items.Add(new MyData { Name = "dumyText", Price = 2 });
}
I have a property defined in Class A. When the property is changed then i need to raise an event. Another class B should respond to this event and do something. I have done something like this:
Code:
Class A{
string variable = "test";
public delegate void EventHandler(object sender, EventArgs args);
public static event EventHandler ThrowAddEvent = delegate { };
public static event EventHandler ThrowRemoveEvent = delegate { };
public String Name { get; set; }
public int Select { get; set; }
public Window Formref1 { get; set; }
public bool IsSelected
{
get { return IsSlctd; }
set
{
IsSlctd = value;
if (value)
{
ThrowAddEvent(this, new EventArgs());
}
else
{
ThrowRemoveEvent(this, new EventArgs());
}
}
}
}
Another class which is responding to this event is defined as follows:
Class B{
public B(ParsedResult _result)
{
InitializeComponent();
if (_result != null)
{
this.Result = _result;
PopulateColumns1();
DataGrid1.Items.Clear();
DataGrid1.ItemsSource = Populatevariables();
}
}
public void PopulateColumns1()
{
DataGridTextColumn Colvar = new DataGridTextColumn();
Colvar.Header = "Variables";
Colvar.Binding = new Binding("Name");
DataGrid1.Columns.Add(Colvar);
DataGridTemplateColumn Col = new DataGridTemplateColumn();
Col.Header = "Select";
DataTemplate dd = new DataTemplate();
FrameworkElementFactory FF = new FrameworkElementFactory(typeof(CheckBox));
FF.SetBinding(CheckBox.BindingGroupProperty, new Binding("Select"));
FF.SetBinding(CheckBox.IsCheckedProperty, new Binding("IsSelected") { UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged });
FF.SetValue(FrameworkElement.HorizontalAlignmentProperty, HorizontalAlignment.Center);
dd.VisualTree = FF;
Col.CellTemplate = dd;
DataGrid1.Columns.Add(Col);
}
private List<A> PopulateVariables()
{
CheckBox cb = new CheckBox();
List<A> CharList = new List<A>();
Result.GetCharacteristicList.MoveNext();
IEnumerator<A2LCharacteristic> enumeratorlist = Result.GetCharacteristicList;
for (int i = 0; enumeratorlist.MoveNext(); i++)
{
CharList.Add(new A() { Name = enumeratorlist.Current.Name, Select = i, Formref1 = this});
}
enumeratorlist.Reset();
return CharList;
}
private void OKBtn_Click(object sender, RoutedEventArgs e)
{
A Instance_A = new A();
Instance_A.ThrowAddEvent += (sender1, args) => { Addvrbl(Instance_A.variable); };
Instance_A.ThrowRemoveEvent += (sender2, args) => { RmvVrbl(Instance_A.variable); };
this.Close();
}
public void Addvrbl(string vrbl)
{
if (!(vrbllist.Contains(vrbl)))
{
vrbllist.Add(vrbl);
}
}
public void RmvVrbl(string vrbl)
{
if ((vrbllist.Contains(vrbl)))
{
vrbllist.Remove(vrbl);
}
}
}
The problem is it is not going inside the method "AddVrbl" and "RmvVrbl". I have used the solution from here. Please help.
OK, instead of subscribing to the event of a new instance of A,which will never get triggered/published. When you initializing CharList, you have to subscribe to the event of each A item.
Something like:
for (int i = 0; enumeratorlist.MoveNext(); i++)
{
var obja=new A() { Name = enumeratorlist.Current.Name, Select = i, Formref1 = this};
obja.ThrowAddEvent += ...
obja.ThrowRemoveEvent += ...
CharList.Add(obja);
}
PS: Make sure you un-subscribe these events before removing an item from your CharList.
I would like to get the form as datasource when i click combo box. How could i achieve this. Please give suggestion.
I've created a PopupComboBox class which inherits from the standard Windows Forms ComboBox. It has a Title property that you can set which will be used on the popup window when the combo is clicked.
public class PopupComboBox : ComboBox
{
private string title;
public string Title
{
get { return this.title; }
set { this.title = value; }
}
public PopupComboBox() : base()
{
}
public PopupComboBox (string title)
: base()
{
this.title = title;
}
protected override void OnMouseClick(MouseEventArgs e)
{
base.OnMouseClick(e);
// Show the popup form
var popup = new SelectItemForm(this.title, this);
var result = popup.ShowDialog(this);
if (result == DialogResult.OK)
{
// Select this item in the ComboBox
SelectedIndex = this.FindStringExact(popup.SelectedDisplay);
}
}
}
When you click on the combo, a popup form will come up (source code below for SelectItemForm). It uses the DataSource, ValueMember and DisplayMember of the parent PopupComboBox to populate a ListView with the list of items from the combo. When you click on OK, it will save the selected item in the SelectedValue and SelectedDisplay properties so that we can select that item in the ComboBox when the form closes.
public partial class SelectItemForm : Form
{
public object SelectedValue { get; private set; }
public string SelectedDisplay { get; private set; }
public SelectItemForm(string title, PopupComboBox parent)
:base()
{
InitializeComponent();
this.Text = title;
// Add items to the list
foreach (var item in parent.Items)
{
// Get the display and value member properties for this combo box
// and use them for the Code/Name columns in the popup form
var props = item.GetType().GetProperties();
var code = props.Where(p => p.Name == parent.ValueMember).Single().GetValue(item);
var name = props.Where(p => p.Name == parent.DisplayMember).Single().GetValue(item);
listView.Items.Add(new ListViewItem(
new string[] { code.ToString(), name.ToString() }));
}
}
private void btnOk_Click(object sender, EventArgs e)
{
if (listView.SelectedItems != null && listView.SelectedItems.Count > 0)
{
SelectedValue = listView.SelectedItems[0].Text;
SelectedDisplay = listView.SelectedItems[0].SubItems[1].Text;
DialogResult = DialogResult.OK;
}
else
{
MessageBox.Show(this, "Select an item first", "Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
}
private void btnCancel_Click(object sender, EventArgs e)
{
DialogResult = DialogResult.Cancel;
}
}
Here is the designer part of the form, where you can see what properties I've changed for the ListView to look like it does:
partial class SelectItemForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.listView = new System.Windows.Forms.ListView();
this.btnOk = new System.Windows.Forms.Button();
this.btnCancel = new System.Windows.Forms.Button();
this.Code = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
this.Value = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
this.SuspendLayout();
//
// listView
//
this.listView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.listView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
this.Code,
this.Value});
this.listView.FullRowSelect = true;
this.listView.GridLines = true;
this.listView.Location = new System.Drawing.Point(3, 3);
this.listView.MultiSelect = false;
this.listView.Name = "listView";
this.listView.Size = new System.Drawing.Size(432, 170);
this.listView.TabIndex = 0;
this.listView.UseCompatibleStateImageBehavior = false;
this.listView.View = System.Windows.Forms.View.Details;
//
// btnOk
//
this.btnOk.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.btnOk.Location = new System.Drawing.Point(272, 179);
this.btnOk.Name = "btnOk";
this.btnOk.Size = new System.Drawing.Size(75, 23);
this.btnOk.TabIndex = 1;
this.btnOk.Text = "OK";
this.btnOk.UseVisualStyleBackColor = true;
this.btnOk.Click += new System.EventHandler(this.btnOk_Click);
//
// btnCancel
//
this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.btnCancel.Location = new System.Drawing.Point(353, 179);
this.btnCancel.Name = "btnCancel";
this.btnCancel.Size = new System.Drawing.Size(75, 23);
this.btnCancel.TabIndex = 2;
this.btnCancel.Text = "Cancel";
this.btnCancel.UseVisualStyleBackColor = true;
this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);
//
// Code
//
this.Code.Text = "Code";
this.Code.Width = 108;
//
// Value
//
this.Value.Text = "Name";
this.Value.Width = 296;
//
// SelectItemForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(440, 214);
this.Controls.Add(this.btnCancel);
this.Controls.Add(this.btnOk);
this.Controls.Add(this.listView);
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "SelectItemForm";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "Title";
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.ListView listView;
private System.Windows.Forms.Button btnOk;
private System.Windows.Forms.Button btnCancel;
private System.Windows.Forms.ColumnHeader Code;
private System.Windows.Forms.ColumnHeader Value;
}
I've also created a small test form where you can test out the functionality of the PopupComboBox, using a small list of products. You'll need to add a PopupComboBox to the form in the designer, and call it comboPopup.
public partial class TestForm : Form
{
public class Product
{
public string Code { get; set; }
public string Name { get; set; }
}
public TestForm()
{
InitializeComponent();
}
private void TestForm_Load(object sender, EventArgs e)
{
var products = new List<Product>();
products.Add(new Product { Code = "0001", Name = "Coca Cola" });
products.Add(new Product { Code = "0002", Name = "Mountain Dew" });
products.Add(new Product { Code = "0003", Name = "Sprite Zero" });
comboPopup.DataSource = products;
comboPopup.DisplayMember = "Name";
comboPopup.ValueMember = "Code";
}
}
Here's how I am making my GridView:
The ListView will contain Entry objects which looks like this:
public class Entry
{
public Entry(BitmapImage icon = null, List<EntryKeyValuePair> entryKeyValuePairs = null)
{
Icon = icon;
EntryKeyValuePairs = entryKeyValuePairs ?? new List<EntryKeyValuePair>();
}
public BitmapImage Icon { get; set; }
public List<EntryKeyValuePair> EntryKeyValuePairs { get; }
}
EntryKeyValuePair is just a KeyValuePair<string,string> where Key is the Column and Value is the value of the column. I used a List of KeyValuePair because I want to preserve insertion order. Anyway, here's how I am constructing the GridView.
GridView = new GridView();
foreach (Column column in Category.Columns.Where(c => c.IsVisibleInTable)) {
var gridViewColumn = new GridViewColumn {
Header = column.Name,
DisplayMemberBinding = new Binding($"EntryKeyValuePairs[{column.Name}].Value")
};
GridView.Columns.Add(gridViewColumn);
}
I don't know what binding to set in DisplayMemberBinding. The above binding would work if EntryKeyValuePairs was a dictionary. But in my case it is not.
If I had access to the data object somehow, I could do
DisplayMemberBinding = new Binding($"EntryKeyValuePairs[{entry.EntryKeyValuePairs.FindIndex(p => p.Key == column.Name)}].Value")
How can I access the current Data Object which the ListView is holding while binding?
I found a solution. I used the GridViewColumn's CellTemplateSelector so that I can get a reference to the ListViews bound object. Here is how the CellTemplateSelector looks like. I had to create the DataTemplates in code.
class GridViewCellTemplateSelector : DataTemplateSelector
{
private readonly string _columnName;
public GridViewCellTemplateSelector(string columnName)
{
_columnName = columnName;
}
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
var entry = (Entry)item;
var dataTemplate = new DataTemplate {
DataType = typeof (Entry)
};
var stackPanelFactory = new FrameworkElementFactory(typeof(StackPanel));
stackPanelFactory.SetValue(StackPanel.OrientationProperty, Orientation.Vertical);
var text = new FrameworkElementFactory(typeof(TextBlock));
text.SetBinding(TextBlock.TextProperty, new Binding($"EntryKeyValuePairs[{entry.EntryKeyValuePairs.FindIndex(p => p.Key == _columnName)}].Value"));
stackPanelFactory.AppendChild(text);
dataTemplate.VisualTree = stackPanelFactory;
return dataTemplate;
}
}
Instead of DisplayMemberBinding, I used this TemplateSelector:
CellTemplateSelector = new GridViewCellTemplateSelector(column.Name)
All good. Hope this helps someone :) I still hope to see a better solution than this.
I got a problem with datagrid and DataGridCheckBoxClumn. First of all im creating struct for datagrid items:
public struct taxRateFromDatabase
{
public int rate { get; set; }
public string mark { get; set; }
public CheckBox c { get; set; }
}
And after that in my class adding columns, bindings etc:
StackPanel tSp = new StackPanel();
DataGrid taxRateDataGrid = new DataGrid();
DataGridTextColumn col0 = new DataGridTextColumn();
DataGridTextColumn col1 = new DataGridTextColumn();
DataGridCheckBoxColumn col2 = new DataGridCheckBoxColumn();
Binding b = new Binding("checkBox");
b.Mode = BindingMode.TwoWay;
b.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
taxRateDataGrid.Columns.Add(col0);
taxRateDataGrid.Columns.Add(col1);
taxRateDataGrid.Columns.Add(col2);
col0.Binding = new Binding("rate");
col1.Binding = new Binding("mark");
col2.Binding = b;
CheckBox c = new CheckBox();
c.Content = "a";
col0.Header = "Stawka";
col1.Header = "Oznaczenie";
col2.Header = "Status";
taxRateDataGrid.Items.Add(new taxRateFromDatabase { rate = 0, mark = "E", c = c });
taxRateDataGrid.Items.Add(new taxRateFromDatabase { rate = 1, mark = "G", c = c });
Problem is that I cant really check/uncheck that checkbox i have just added.
I have tried also without checkbox in struct definition (just empty datagridcheckboxcolumn), but that also doesnt work. Im creating it in class which will return datagrid so i cant really acces xaml.
Any sugestions will be appreciated ;)
I suggest you to use class instead struct (take a look here) and implement INotifyPropertyChanged interface in order to get the binding working.
Something like
public class TaxRateFromDatabase : INotifyPropertyChanged
{
private int _rate;
public int Rate
{
get { return _rate; }
set { _rate = value; OnPropertyChanged("Rate"); }
}
private string _mark;
public string Mark
{
get { return _mark; }
set { _mark = value; OnPropertyChanged("Mark"); }
}
private bool _isChecked;
public bool IsChecked
{
get { return _isChecked; }
set { _isChecked = value; OnPropertyChanged("IsChecked"); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
and for example
DataGrid taxRateDataGrid = new DataGrid();
DataGridTextColumn col0 = new DataGridTextColumn();
DataGridTextColumn col1 = new DataGridTextColumn();
DataGridCheckBoxColumn col2 = new DataGridCheckBoxColumn();
taxRateDataGrid.Columns.Add(col0);
taxRateDataGrid.Columns.Add(col1);
taxRateDataGrid.Columns.Add(col2);
col0.Binding = new Binding("Rate");
col1.Binding = new Binding("Mark");
col2.Binding = new Binding("IsChecked");
col0.Header = "Stawka";
col1.Header = "Oznaczenie";
col2.Header = "Status";
List<TaxRateFromDatabase> list = new List<TaxRateFromDatabase>();
list.Add(new TaxRateFromDatabase { Rate = 1, Mark = "E", IsChecked = true });
list.Add(new TaxRateFromDatabase { Rate = 23, Mark = "F", IsChecked = false });
taxRateDataGrid.ItemsSource = list;