Im trying to bind one combobox from my class that i linked in the context
i have this in the control
<dxe:ComboBoxEdit Width="100" Margin="5" Name="cboProduct" DisplayMember="Name"
SelectedItem="{Binding Path=DataContext.SelectedProduct, Mode=OneWay, RelativeSource={RelativeSource AncestorType=Window}}"
>
</dxe:ComboBoxEdit>
I filled the Combobox befor from code behind like this
var lsproducts = new List<Product>();
var Products =_licenseService.GetProductList();
Products.ForEach((x) => {
lsproducts.Add(new Product(x.Name, x.ProductId));
});
And im setting the SelectedProduct like this
[DataMember]
public License SelectedLicense {
get { return _SelectedLicense;}
set {
_SelectedLicense = value;
this.NotifyPropertyChanged("SelectedLicense");
}
}
public Product SelectedProduct
{
get
{
return new Product(_SelectedLicense.Product.Name,_SelectedLicense.Product.ProductId);
}
}
this.cboProduct.ItemsSource = lsproducts.ToArray();
in both cases im using the object Product
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using pd.Common.Domain;
namespace LicenceManagerWPF.Views
{
public class Product
{
public string Name { get; set; }
public ProductEnum ProductID { get; set; }
public Product(string ProductName,ProductEnum ID)
{
Name = ProductName;
ProductID = ID;
}
}
i dont know why its not selecting the product when i open the window.
i have another one
[![Shows like this][2]][2]
I dont know why its display the x mark, but when i chouse another licence it updated the combo selection
<dxe:ComboBoxEdit x:Name="cboActivationMode" Grid.Row="0" Grid.Column="1" HorizontalAlignment="Left" Width="100" Style="{StaticResource TabMargin}"
SelectedItem="{Binding Path=DataContext.SelectedLicense.ActivationMode, RelativeSource={RelativeSource AncestorType=Window}}"
/>
The second one its only a couple of enums values that fill like this.
cboActivationMode.Items.Add(
new DevExpress.Xpf.Editors.ComboBoxEditItem()
{ Content = Enum.GetName(typeof(ActivationMode), ActivationMode.Online), Tag = ActivationMode.Online });
How can i bind the values to a combobox?
Regards
cboActivationMode.Items.Add(
new DevExpress.Xpf.Editors.ComboBoxEditItem()
{Content = Enum.GetName(typeof(ActivationMode), ActivationMode.Offline),Tag = ActivationMode.Offline});
I tryed to do this
public partial class LicenseDetail : UserControl
{
private readonly LicenseService _licenseService;
public LicenseDetail()
{
InitializeComponent();
_licenseService = new LicenseService();
FillCombos();
}
private void FillCombos()
{
FillProducts();
FillActivationMode();
//var str = Enum.GetName(typeof(ProductEnum), ProductEnum.CDSAddIn);
//this.cboProduct.ItemsSource = new string[] { str };
}
private void FillProducts()
{
var Products = _licenseService.GetProductList();
cboProduct.ItemsSource = Products;
}
The product list is a list of Iproduct (interface), i dont get i why they made it like this but each product is diferent they implement the same baseclass and the interface
[DataContract]
public class ProductList : List<IProduct>
{
public bool Contains(ProductEnum productId)
{
return this.Any(x => x.ProductId == productId);
}
public IProduct GetProduct(ProductEnum productId)
{
return this.FirstOrDefault(x => x.ProductId == productId);
}
public void Remove(ProductEnum productId)
{
Remove(GetProduct(productId));
}
}
I changed the combo to bind like this
SelectedItem="{Binding Path=DataContext.MyProduct, RelativeSource={RelativeSource AncestorType=Window}}"
I create the property in the class like this
public IProduct MyProduct
{
get { return _MyProduct; }
set
{
_MyProduct = value;
this.NotifyPropertyChanged("MyProduct");
}
}
And assaing like this
_CustomerLicense.MyProduct = SelectedLicense.Product;
this is how the list of products is filled
public IProduct GetProduct(ProductEnum productId)
{
IProduct product = null;
var connection = GetConnection();
try
{
var sql = string.Format(Resources.GetProduct, (int)productId);
var cmd = new SqlCommand(sql, connection) { CommandType = CommandType.Text, Transaction = _transaction };
using (var rdr = new NullableDataReader(cmd.ExecuteReader()))
while (rdr.Read())
{
var productName = rdr.GetString(0);
var featureId = rdr.GetInt32(1);
var featureDesc = rdr.GetString(2);
if (product == null)
{
switch (productId)
{
case ProductEnum.PDCalc:
product = new PDCalcProduct(productId, productName);
break;
case ProductEnum.PDMaint:
product = new PDMaintProduct(productId, productName);
break;
case ProductEnum.PBDynamics:
product = new PBDynamicsProduct(productId, productName);
break;
case ProductEnum.CDSAddIn:
product = new CDSAddInProduct(productId, productName);
break;
}
}
if (product != null)
product.Features.Add(new Feature((FeatureEnum)featureId, featureDesc));
}
}
finally
{
CloseConnection(connection);
}
return product;
}
without any luck.
Regards
The SelectedProduct property should have a public setter for you to be able to set it to the currently selected value in the ComboBox:
private Product _selectedProduct;
public Product SelectedProduct
{
get { return _selectedProduct; }
set
{
_selectedProduct = value;
this.NotifyPropertyChanged("SelectedProduct");
}
}
And for the intial value to be selected, you either need to set it to a Product object that is actually in the ItemsSource (lsproducts):
viewModel.SelectedProduct = lsproducts.FirstOrDefault(x => x.Name == _SelectedLicense.Product.Name && x.ProductID == _SelectedLicense.Product.ProductId);
Or you will have to override the Equals method of your Product class:
public class Product
{
public string Name { get; set; }
public ProductEnum ProductID { get; set; }
public Product(string ProductName, ProductEnum ID)
{
Name = ProductName;
ProductID = ID;
}
public override bool Equals(object obj)
{
Product other = obj as Product;
return other != null && Name == other.Name && ProductID == other.ProductID;
}
}
I fixed like this:
I created the method :
private void GetProducts()
{
var Products = new LicenseService().GetProductList();
Products.ForEach((x) =>
{
lsproducts.Add(new Product(x.Name, x.ProductId));
});
//this.cboProduct.ItemsSource = lsproducts.ToArray();
}
Then i attached to the load of the main windows, where all the controls are
public frmCustomerLicense(CustomerLicenses cl)
{
InitializeComponent();
GetProducts();
_CustomerLicense = cl;
grdLicenses.grdLicences.SelectedItemChanged += GridRowSelected;
}
Then when one of the licenses is selected i set all the bindings
var Record = (DataRowView)grdLicenses.grdLicences.SelectedItem;
var SelectedLicense = (License)Record["License"];
var list = new LicenseService().GetActivityLog(SelectedLicense.SerialNumber)
.OrderByDescending(x => x.ActivityDate)
.ToList();
_CustomerLicense.ActivityLog = list;
_CustomerLicense.Features = new LicenseService().GetFeatures(SelectedLicense.Product.ProductId);
_CustomerLicense.Products = lsproducts;
_CustomerLicense.HasExpDate = SelectedLicense.HasExpirationDate;
//_CustomerLicense.SetLog(list);
_CustomerLicense.SelectedLicense = SelectedLicense;
//_CustomerLicense.SelectedMaintenance = SelectedLicense.Product.ProductId == ProductEnum.PDMaint ? true : false;
_CustomerLicense.SelectedProduct = lsproducts.FirstOrDefault((x) => x.ProductID == SelectedLicense.Product.ProductId);
And in my ViewClass i added this
[DataMember]
public Product SelectedProduct
{
get { return _SelectedProduct; }
set
{
_SelectedProduct = value;
this.NotifyPropertyChanged("SelectedProduct");
}
}
[DataMember]
public List<Product> Products
{
get { return _Products; }
set { _Products = value;
this.NotifyPropertyChanged("Products");
}
}
So, i set the combobox
<dxe:ComboBoxEdit Width="180" Margin="5" Name="cboProduct" DisplayMember="Name"
ItemsSource="{Binding Path=DataContext.Products, RelativeSource={RelativeSource AncestorType=Window}}"
SelectedItem="{Binding Path=DataContext.SelectedProduct, RelativeSource={RelativeSource AncestorType=Window}}"
>
</dxe:ComboBoxEdit>
Doing this works, thanks for your help mm8
Related
Being new to WPF and MVVM I've been struggling for the last few days trying to solve this issue. I've searched all over stackoverflow and google/Youtube for help.
I have a DataGrid (biound from OrderListView) that is populated from a BindableCollection (Caliburn Micro) of a model. However I need to bring in a property ('Program') of linked data from another BindableCollection ProductList, (both collections share a common property 'Code'.
Basically I want the DataGrid to show all the OrderModel based columns and fill a column called Programs with the related data from the Products collection just at run time.
OrderModel.cs
public class OrderModel : BaseModel
{
private DateTime _orderDate;
public DateTime OrderDate
{
get { return _orderDate; }
set { _orderDate = value; OnPropertyChanged(); }
}
private string _code;
public string Code
{
get { return _code; }
set { _code = value; OnPropertyChanged(); }
}
private int _qty;
public int Qty
{
get { return _qty; }
set { _qty = value; OnPropertyChanged(); }
}
ProductModel.cs
public class ProductModel : BaseModel
{
private string _code;
public string Code
{
get { return _code; }
set { _code = value; OnPropertyChanged(); }
}
private int _program;
public int Program
{
get { return _program; }
set { _program = value; OnPropertyChanged(); }
}
DataGrid in OrderView.xaml
<DataGrid ItemsSource="{Binding OrderListView}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Code}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Qty}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Program}"/> <- This from ProductList ??
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
OrderViewModel.cs
public class OrderViewModel : Screen
{
private readonly IDataConnection _connect;
private ICollectionView _orderView;
public ICollectionView OrderListView
{
get => _orderView;
set
{
_orderView = value;
NotifyOfPropertyChange(() => OrderListView);
}
}
private BindableCollection<OrderModel> _orderList;
public BindableCollection<OrderModel> OrderList
{
get => _orderList;
set
{
_orderList = value;
NotifyOfPropertyChange(() => OrderList);
}
}
private BindableCollection<ProductModel> _productList;
public BindableCollection<ProductModel> ProductList
{
get { return _productList; }
set { _productList = value; }
}
private string _code;
public string Code
{
get { return _code; }
set { _code = value; NotifyOfPropertyChange(() => Code); }
}
private int _qty;
public int Qty
{
get { return _qty; }
set { _qty = value; NotifyOfPropertyChange(() => Qty); }
}
private int _program;
public int Program
{
get { return _program; }
set
{
_program = value;
NotifyOfPropertyChange(() => Program);
}
}
public OrderViewModel(IDataConnection connect)
{
DisplayName = "Orders";
var allOrders = await _connect.Orders_GetByDateRange(StartDate, EndDate);
OrderList = new BindableCollection<OrderModel>(allOrders);
OrderListView = CollectionViewSource.GetDefaultView(OrderList);
var allProducts = await _connect.Products_GetAll();
ProductList = new BindableCollection<ProductModel>(allProducts);
}
}
Basically where 'Code' Matches in the models i want to pull the associated Program into the column.
The ItemsSource that a DataGrid binds to needs to contain all the properties for the column bindings.
Which means either a Program property needs to be added to your current OrderModel or create a new model that contains both properties.
public class OrderModel : BaseModel
{
private DateTime _orderDate;
public DateTime OrderDate
{
get { return _orderDate; }
set { _orderDate = value; OnPropertyChanged(); }
}
private string _code;
public string Code
{
get { return _code; }
set { _code = value; OnPropertyChanged(); }
}
private int _qty;
public int Qty
{
get { return _qty; }
set { _qty = value; OnPropertyChanged(); }
}
private int _program;
public int Program
{
get { return _program; }
set { _program = value; OnPropertyChanged(); }
}
}
Then there will be logic needed to link the new Program value with the value from the other collection.
The cleanest place for this would most likely be to modify your query (assuming you are using a database connection)
Most likely this would be to include a JOIN when doing the selection for the Orders.
However, doing this on the client side, meaning after the data has been recieved from the IDataConnection would look like this:
using System.Linq;
...
public OrderViewModel(IDataConnection connect)
{
DisplayName = "Orders";
var allOrders = await _connect.Orders_GetByDateRange(StartDate, EndDate);
var allProducts = await _connect.Products_GetAll();
ProductList = new BindableCollection<ProductModel>(allProducts);
foreach(var order in allOrders)
{
//assumes "Code" is unique within `ProductList`
order.Program = ProductList.Single(p => p.Code == order.Code);
}
OrderList = new BindableCollection<OrderModel>(allOrders);
OrderListView = CollectionViewSource.GetDefaultView(OrderList);
}
Hi am struggling with validation with data annotations in WPF
I have a class in a separate project because we are reusing the class across multiple projects.Several of the fields have data annotations for validation.
the validation class combines the errors returned from data annotations with some custom business rules which are different per client
internal class Validation<T> : ValidationRule where T : class, ICore
{
IUnitofWork unitofWork;
List<Func<T, bool>> CompiledRules = new List<Func<T, bool>>();
List<Rule<T>> Rules = new List<Rule<T>>();
Dictionary<string, List<string>> _errors = new Dictionary<string, List<string>>();
List<Func<Object, bool>> FieldRules = new List<Func<object, bool>>();
public Validation()
{
unitofWork = new UnitOfWork();
CompileRules();
}
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
return IsValid((T)value);
}
public ValidationResult Validate(object value,string PropertyName ,CultureInfo cultureInfo)
{
return IsValid((T)value,PropertyName);
}
void CompileRules()
{
Type t = typeof(T);
List<BusinessRule> rules = unitofWork.Repository<BusinessRule>().GetAll(b => b.Name == t.Name && b.Enabled == true);
foreach (BusinessRule b in rules)
{
Func<T, bool> CompiledRule = CompileRule(b);
CompiledRules.Add(CompiledRule);
Rule<T> rule = new Rule<T>();
rule.CompiledRule = CompiledRule;
rule.ErrorMessage = b.MessageTemplate;
rule.FieldName = b.Field;
Rules.Add(rule);
}
}
ValidationResult IsValid(T Value)
{
bool valid = true;
_errors.Clear();
if (CompiledRules.Count > 0 || Value.Errors.Count > 0)
{
//valid = CompiledRules.All(rule => rule(Value));
foreach (Rule<T> r in Rules)
{
bool isValid = r.CompiledRule(Value);
r.PassedRule = isValid;
string field = r.FieldName;
if (!isValid )
{
valid = false;
string ErrorMessage = string.Format("{0} {1}", r.FieldName, r.ErrorMessage);
if (_errors.ContainsKey(field))
_errors[field].Add(ErrorMessage);
else
{
List<string> el = new List<string>();
el.Add(ErrorMessage);
_errors.Add(field, el);
}
}
}
}
ValidateAnnotations(Value,ref valid);
return new ValidationResult(valid, _errors);
}
void ValidateAnnotations(object Value,ref bool Valid)
{
DataAnnotationValidator annotationValidator = new DataAnnotationValidator();
List< System.ComponentModel.DataAnnotations.ValidationResult> results = annotationValidator.ValidateObject(Value);
if (results.Count > 0)
{
Valid = false;
foreach (System.ComponentModel.DataAnnotations.ValidationResult r in results)
{
if (_errors.ContainsKey(r.MemberNames.First()))
{
_errors[r.MemberNames.First()].Add(r.ErrorMessage);
}
else
{
List<string> propErrors = new List<string>();
propErrors.Add(r.ErrorMessage);
_errors.Add(r.MemberNames.First(), propErrors);
}
}
}
}
void ValidateAnnotations(object Value,string PropertyName, ref bool Valid)
{
DataAnnotationValidator annotationValidator = new DataAnnotationValidator();
List<System.ComponentModel.DataAnnotations.ValidationResult> results = annotationValidator.ValidateObject(Value, PropertyName);
if (results.Count > 0)
{
Valid = false;
foreach (System.ComponentModel.DataAnnotations.ValidationResult r in results)
{
if (_errors.ContainsKey(r.MemberNames.First()))
{
_errors[r.MemberNames.First()].Add(r.ErrorMessage);
}
else
{
List<string> propErrors = new List<string>();
propErrors.Add(r.ErrorMessage);
_errors.Add(r.MemberNames.First(), propErrors);
}
}
}
}
ValidationResult IsValid(T Value, string PropertyName)
{
_errors.Remove(PropertyName);
bool valid = true;
if (CompiledRules.Count > 0)
{
//valid = CompiledRules.All(rule => rule(Value));
foreach (Rule<T> r in Rules.Where(b=>b.FieldName == PropertyName))
{
bool isValid = r.CompiledRule(Value);
r.PassedRule = isValid;
string field = r.FieldName;
// string field = "SelectedRow." + r.FieldName;
if (!isValid)
{
valid = false;
string ErrorMessage = string.Format("{0} {1}", r.FieldName, r.ErrorMessage);
if (_errors.ContainsKey(field))
_errors[field].Add(ErrorMessage);
else
{
List<string> el = new List<string>();
el.Add(ErrorMessage);
_errors.Add(field, el);
}
}
}
}
ValidateAnnotations(Value,PropertyName, ref valid);
return new ValidationResult(valid, _errors);
}
public Func<T, bool> CompileRule(BusinessRule r)
{
var paramT = Expression.Parameter(typeof(T));
Expression expression = BuildExpr(r, paramT);
return Expression.Lambda<Func<T, bool>>(expression, paramT).Compile();
}
static Expression BuildExpr(BusinessRule r, ParameterExpression param)
{
var left = MemberExpression.Property(param, r.Field);
var tProp = typeof(T).GetProperty(r.Field).PropertyType;
ExpressionType tBinary;
// is the operator a known .NET operator?
if (ExpressionType.TryParse(r.Operator, out tBinary))
{
var right = Expression.Constant(Convert.ChangeType(r.CompareValue, tProp));
// use a binary operation, e.g. 'Equal' -> 'u.Age == 15'
return Expression.MakeBinary(tBinary, left, right);
}
else
{
var method = tProp.GetMethod(r.Operator);
var tParam = method.GetParameters()[0].ParameterType;
var right = Expression.Constant(Convert.ChangeType(r.CompareValue, tParam));
// use a method call, e.g. 'Contains' -> 'u.Tags.Contains(some_tag)'
return Expression.Call(left, method, right);
}
}
}
internal class Rule<T> where T : class, ICore
{
public string FieldName
{ get;set; }
public Func<T, bool> CompiledRule
{ get; set; }
public Func<object,bool> RevisedRule
{ get; set; }
public string ErrorMessage
{ get; set; }
public bool PassedRule
{ get; set; }
}
internal class Error
{
public string ErrorMessage { get; set; }
public string FieldName { get; set; }
}
The is working as the _errors list has the field names and any errors associated with them.
once we have these we loop through and raise the onPropertyErrorsChangedEvent
internal void SetErrorDetails(ValidationResult Result)
{
propErrors = (Dictionary<string, List<string>>)Result.ErrorContent;
foreach (string key in propErrors.Keys)
{
OnPropertyErrorsChanged(key);
}
}
on my view the 2 fields are
<TextBox Canvas.Left="138"
Canvas.Top="75"
FontFamily="Verdana"
HorizontalAlignment="Left"
Height="20"
Text="{Binding OrganizationName,ValidatesOnDataErrors=True,NotifyOnValidationError=True,ValidatesOnNotifyDataErrors=True,ValidatesOnExceptions=True}" VerticalAlignment="Top" Width="137"
Validation.ErrorTemplate="{StaticResource ValidationTemplate }"
Style="{StaticResource TextErrorStyle}"/>
<TextBox Canvas.Left="138"
Canvas.Top="225"
FontFamily="Verdana"
HorizontalAlignment="Left"
Height="20"
Text="{Binding SelectedRow.Postcode,ValidatesOnDataErrors=True,ValidatesOnNotifyDataErrors=True,ValidatesOnExceptions=True}"
VerticalAlignment="Top"
Width="137"
Validation.ErrorTemplate="{StaticResource ValidationTemplate }"
Style="{StaticResource TextErrorStyle}" />
I am encountering 2 problems i am encountering
When bound directly to the selectedrow (Postcode) my data annotations appear when the form is loaded but when bound via a field on my view model (Organisation Name) they do not . We need to bind these to fields on the view model so that the business rules get run as part of validation.
Second problem if i save the form with an invalid entry for the organisation the save stops because it is invalid however i don't get an error notification even though there is an error for the property in the _errors.
I am not sure what i am doing wrong could someone point me in the right direction please?
[Edit]
We use a third party document service to create and show the view
void CreateDocument(object Arg)
{
string title = string.Empty;
if (Arg.ToString().ToLower() == "edit" && SelectedRow !=null)
{
if (SelectedRow.OrganizationName != null)
title = SelectedRow.OrganizationName;
}
else
{
SelectedRow = new Address();
title = "New Address";
}
AddressDetailVM detail = new AddressDetailVM(SelectedRow,this);
Document = iInternal.CreateDocument("AddressDetails",
detail,
title
);
detail.Document = Document;
// Document = iInternal.CreateDocument("AddressDetails", null, this, title);
Document.Show();
}
as the topic suggests I wan't to modify the Content of the CollectionEditorPicker. This control is used to open the floating Window for the List of nested Properties.
Unfortunally the RadPropertyGrid don't show any Information about the collection in the Field.
How can I set some value in there? For example a placeholder like "Click here to open the collection" or "xx Items" or "Item 1, Item 2, Item 3..." so see some preview or Information about the field.
I've tried it with a template Selector, but if I'm doing so, the opened Popup is not resizable anymore. Also it looses some Information which are in the default CollectionEditorPicker.
Can you help me?
Below a minimal working Example.
The XAML:
<Window x:Class="TelerikPropertyGridTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
xmlns:model="clr-namespace:TelerikPropertyGridTest.Model"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.Resources>
<model:TemplateSelector x:Key="RadPropertyListTemplateSelector">
<!-- Not Working -->
<model:TemplateSelector.CollectionsDataTemplate>
<DataTemplate>
<telerik:RadDropDownButton Content="Test">
<telerik:RadDropDownButton.DropDownContent>
<telerik:CollectionEditor telerik:AutoBindBehavior.UpdateBindingOnElementLoaded="Source"
></telerik:CollectionEditor>
</telerik:RadDropDownButton.DropDownContent>
</telerik:RadDropDownButton>
</DataTemplate>
</model:TemplateSelector.CollectionsDataTemplate>
<model:TemplateSelector.FloatNumberTemplate>
<DataTemplate>
<telerik:RadNumericUpDown telerik:AutoBindBehavior.UpdateBindingOnElementLoaded="Value" />
</DataTemplate>
</model:TemplateSelector.FloatNumberTemplate>
<model:TemplateSelector.IntNumberTemplate>
<DataTemplate>
<telerik:RadNumericUpDown telerik:AutoBindBehavior.UpdateBindingOnElementLoaded="Value"
NumberDecimalDigits="0" />
</DataTemplate>
</model:TemplateSelector.IntNumberTemplate>
</model:TemplateSelector>
</Grid.Resources>
<telerik:RadPropertyGrid Item="{Binding ObjectToBind}"
AutoGeneratingPropertyDefinition="RadPropertyGrid_OnAutoGeneratingPropertyDefinition"
EditorTemplateSelector="{StaticResource RadPropertyListTemplateSelector}">
</telerik:RadPropertyGrid>
</Grid>
</Window>
The ViewModel (generates a Random Object for testing)
public class MainWindowViewModel : BindableBase
{
private readonly Random _random = new Random();
private IExampleInterface _objectToBind;
public MainWindowViewModel()
{
this.ObjectToBind = new ExampleImplementation
{
SomeBooleanValue = this._random.Next() % 2 == 1,
SomeDateValue = this.RandomDay(),
SomeIntValue = this._random.Next(),
SomeString = Guid.NewGuid().ToString(),
SubClasses = new List<IExampleInterface>
{
new ExampleImplementation
{
SomeBooleanValue = this._random.Next() % 2 == 1,
SomeDateValue = this.RandomDay(),
SomeIntValue = this._random.Next(),
SomeString = Guid.NewGuid().ToString(),
SubClasses = new List<IExampleInterface>
{
new ExampleImplementation
{
SomeBooleanValue =
this._random.Next() % 2 == 1,
SomeDateValue = this.RandomDay(),
SomeIntValue = this._random.Next(),
SomeString = Guid.NewGuid().ToString()
}
}
}
}
};
}
public IExampleInterface ObjectToBind
{
get { return this._objectToBind; }
set
{
if (this._objectToBind != value)
{
this._objectToBind = value;
this.OnPropertyChanged("ObjectToBind");
}
}
}
private DateTime RandomDay()
{
var start = new DateTime(1995, 1, 1);
var range = (DateTime.Today - start).Days;
return start.AddDays(this._random.Next(range));
}
}
The IExampleInterface (should be later on a real Interface):
public interface IExampleInterface
{
string SomeString { get; set; }
int SomeIntValue { get; set; }
double SomeDouble { get; set; }
IList<IExampleInterface> SubClasses { get; set; }
IList<IExampleInterface> SubClasses2 { get; set; }
bool SomeBooleanValue { get; set; }
DateTime SomeDateValue { get; set; }
SomeEnum SomeEnumValue { get; set; }
}
The ExampleImplementation (should have later on a Real Implementation with additional Properties).
public class ExampleImplementation : BindableBase, IExampleInterface
{
private bool _someBooleanValue;
private DateTime _someDateValue;
private double _someDouble;
private SomeEnum _someEnumValue;
private int _someIntValue;
private string _someString;
private ObservableCollection<IExampleInterface> _subClasses;
private ObservableCollection<IExampleInterface> _subClasses2;
public bool SomeBooleanValue
{
get { return this._someBooleanValue; }
set
{
if (this._someBooleanValue != value)
{
this._someBooleanValue = value;
this.OnPropertyChanged("SomeBooleanValue");
}
}
}
public DateTime SomeDateValue
{
get { return this._someDateValue; }
set
{
if (this._someDateValue != value)
{
this._someDateValue = value;
this.OnPropertyChanged("SomeDateValue");
}
}
}
public double SomeDouble
{
get { return this._someDouble; }
set
{
if (Math.Abs(this._someDouble - value) > 0.01)
{
this._someDouble = value;
this.OnPropertyChanged("SomeDouble");
}
}
}
public SomeEnum SomeEnumValue
{
get { return this._someEnumValue; }
set
{
if (this._someEnumValue != value)
{
this._someEnumValue = value;
this.OnPropertyChanged("SomeEnumValue");
}
}
}
public int SomeIntValue
{
get { return this._someIntValue; }
set
{
if (this._someIntValue != value)
{
this._someIntValue = value;
this.OnPropertyChanged("SomeIntValue");
}
}
}
[Display(Name = #"TestString", GroupName = #"TestGroup", Description = #"TestDescription")]
public string SomeString
{
get { return this._someString; }
set
{
if (this._someString != value)
{
this._someString = value;
this.OnPropertyChanged("SomeString");
}
}
}
[Display(Name = #"Some Subclasses")]
public IList<IExampleInterface> SubClasses
{
get { return this._subClasses; }
set
{
if (!Equals(this._subClasses, value))
{
this._subClasses = new ObservableCollection<IExampleInterface>(value);
this.OnPropertyChanged("SubClasses");
}
}
}
public IList<IExampleInterface> SubClasses2
{
get { return this._subClasses2; }
set
{
if (!Equals(this._subClasses2, value))
{
this._subClasses2 = new ObservableCollection<IExampleInterface>(value);
this.OnPropertyChanged("SubClasses2");
}
}
}
}
And finally the TemplateSelector
public class TemplateSelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
var def = item as PropertyDefinition;
if (def == null || def.SourceProperty == null)
{
return base.SelectTemplate(item, container);
}
if (typeof (IEnumerable).IsAssignableFrom(def.SourceProperty.PropertyType) && typeof(string) != def.SourceProperty.PropertyType)
{
return this.CollectionsDataTemplate;
}
if (typeof (double).IsAssignableFrom(def.SourceProperty.PropertyType))
{
return this.FloatNumberTemplate;
}
if (typeof (int).IsAssignableFrom(def.SourceProperty.PropertyType))
{
return this.IntNumberTemplate;
}
return base.SelectTemplate(item, container);
}
public DataTemplate CollectionsDataTemplate { get; set; }
public DataTemplate FloatNumberTemplate { get; set; }
public DataTemplate IntNumberTemplate { get; set; }
}
This is what I expect
The optimal solution would be to get detailed Information in the TextBlock, like Item 1, item 2 etc.
Thank you.
// Edit:
I've figured out the NullReferenceException and got a Demo to work, so that I can modify the text. But the popup is different to the default. Have you an idea to fix it?
I've updated the text and the example.
After wasting a few hours now I figured out a solution to realize this.
I've added a custom behavior to the Collection template. This behavior sets the Header of the CollectionEditor as soon as it's loaded or updated.
Below you can see my modifications:
The Template:
<model:TemplateSelector.CollectionsDataTemplate>
<DataTemplate>
<telerik:RadDropDownButton Content="Click to edit the collection">
<telerik:RadDropDownButton.DropDownContent>
<telerik:CollectionEditor telerik:AutoBindBehavior.UpdateBindingOnElementLoaded="Source"
ResizeGripperVisibility="Visible">
<i:Interaction.Behaviors>
<model:CollectionEditorBehavior />
</i:Interaction.Behaviors>
</telerik:CollectionEditor>
</telerik:RadDropDownButton.DropDownContent>
</telerik:RadDropDownButton>
</DataTemplate>
</model:TemplateSelector.CollectionsDataTemplate>
The behavior:
internal class CollectionEditorBehavior : Behavior<CollectionEditor>
{
protected override void OnAttached()
{
this.AssociatedObject.SourceUpdated += (sender, args) => this.PrepareHeader();
this.AssociatedObject.DataContextChanged += (sender, args) => this.PrepareHeader();
this.AssociatedObject.Loaded += (sender, args) => this.PrepareHeader();
}
private void PrepareHeader()
{
if (this.AssociatedObject == null)
{
// Error Case
return;
}
if (this.AssociatedObject.CollectionView == null ||
this.AssociatedObject.CollectionView.SourceCollection == null)
{
// Source not set
this.AssociatedObject.Header = "Collection";
return;
}
// Get the property from the DataContext to retrieve HeaderInformation
var propInfo = this.AssociatedObject.DataContext
.GetType()
.GetProperties()
.FirstOrDefault(
propertyInfo =>
Equals(propertyInfo.GetValue(this.AssociatedObject.DataContext),
this.AssociatedObject.CollectionView.SourceCollection));
if (propInfo == null)
{
// We didn't got the property Information, using default value
this.AssociatedObject.Header = "Collection";
return;
}
// Getting the DisplayName Attribute
var attr = Attribute.GetCustomAttribute(propInfo,
typeof (DisplayNameAttribute)) as DisplayNameAttribute;
if (attr != null)
{
// We have a DisplayName attribute
this.AssociatedObject.Header = attr.DisplayName;
return;
}
// Alternative: Get the Display Attribute
var attr2 = Attribute.GetCustomAttribute(propInfo,
typeof (DisplayAttribute)) as DisplayAttribute;
if (attr2 != null)
{
// We have the Display Attribute
this.AssociatedObject.Header = attr2.Name;
return;
}
// We have no DisplayAttribute and no DisplayName attribute, set it to the PropertyName
this.AssociatedObject.Header = propInfo.Name;
}
}
namespace colourchanges
{
public class Group
{
public string Name { get; set; }
//its a class for adding parent list using group class
}
public class EmployeeTree : INotifyPropertyChanged
{
public EmployeeTree()
{
this.GroupStaff = new List<Group>();
GroupStaff.Add(new Group { Name = "Designers" });
GroupStaff.Add(new Group { Name = "Developers" });
GroupStaff.Add(new Group { Name = "Managers" });
//here we are declaring list for adding parent list
}
private List<Group> _GroupStaff;
public List<Group> GroupStaff
{
get { return _GroupStaff; }
set
{
_GroupStaff = value;
RaisePropertyChanged("GroupStaff");
}
}
//creates a list for parentlist
private Group _selectedGroupStaff;
public Group selectedGroupStaff
{
get { return _selectedGroupStaff; }
set
{
_selectedGroupStaff = value;
if (selectedGroupStaff.Name == "Designers")
{
City = "Chennai";
Country = "India";
Email = "Designer#gmail.com";
MobileNo = 9094117917;
Address = "Annanagar";
}
else if (selectedGroupStaff.Name == "Developers")
{
City = "Trichy";
Country = "India";
Email = "Developer#gmail.com";
MobileNo = 9094667878;
Address = "Koyambedu";
}
else if (selectedGroupStaff.Name == "Managers")
{
City = "Salem";
Country = "India";
Email = "Manager#gmail.com";
MobileNo = 9094154678;
Address = "Arumbakkam";
}
RaisePropertyChanged("selectedGroupStaff");
}
}//for selecting parent list in order to bind to textbox
private string _City;
private string _Country;
private string _Email;
private long _MobileNo;
private string _Address;
//properties of parent list to bind to textbox
public string City
{
get { return _City; }
set
{
_City = value;
RaisePropertyChanged("City");
}
}
public string Country
{
get { return _Country; }
set
{
_Country = value;
RaisePropertyChanged("Country");
}
}
public string Email
{
get { return _Email; }
set
{
_Email = value;
RaisePropertyChanged("Email");
}
}
public long MobileNo
{
get { return _MobileNo; }
set
{
_MobileNo = value;
RaisePropertyChanged("MobileNo");
}
}
public string Address
{
get { return _Address; }
set
{
_Address = value;
RaisePropertyChanged("Address");
}
}
///raise property changed event handler code
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
//how to add sub list for designers developers and managers in the constructor
Let the following be your Model class
public class Group
{
private string _City;
private string _Country;
private string _Email;
private long _MobileNo;
private string _Address;
public Group()
{
Items = new ObservableCollection<Group>();
}
public ObservableCollection<Group> Items { get; set; }
}
and at the ViewModel's constructor, you can add the Items.
public EmployeeTree()
{
this.GroupStaff = new List<Group>();
Group rootGroup = new Group(){Name ="Manager"};
Group childGroup = new Group(){Name = "Developer"};
rootGroup.Items.Add(childGroup);
this.GroupStaff.Add(rootGroup);
}
This is for Hierarchical structure. Hope you are looking for this.
And your XAML should be like this
<TreeView Name="GroupTreeView">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Items}">
<TextBlock Text="{Binding Name}" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
I have a textbox and a Datagrid. The datagrid has two columns name and Email address. I want to Filter the datagrid values with the value in the textbox.
You can use a ICollectionView for the DataGrid ItemSource then you can apply a Filter predicate and refesh the list when needed.
Here is a very quick example.
Xaml:
<Window x:Class="WpfApplication10.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="188" Width="288" Name="UI" >
<StackPanel DataContext="{Binding ElementName=UI}">
<TextBox Text="{Binding FilterString, UpdateSourceTrigger=PropertyChanged}" />
<DataGrid ItemsSource="{Binding DataGridCollection}" />
</StackPanel>
</Window>
Code:
namespace WpfApplication10
{
public partial class MainWindow : Window, INotifyPropertyChanged
{
private ICollectionView _dataGridCollection;
private string _filterString;
public MainWindow()
{
InitializeComponent();
DataGridCollection = CollectionViewSource.GetDefaultView(TestData);
DataGridCollection.Filter = new Predicate<object>(Filter);
}
public ICollectionView DataGridCollection
{
get { return _dataGridCollection; }
set { _dataGridCollection = value; NotifyPropertyChanged("DataGridCollection"); }
}
public string FilterString
{
get { return _filterString; }
set
{
_filterString = value;
NotifyPropertyChanged("FilterString");
FilterCollection();
}
}
private void FilterCollection()
{
if (_dataGridCollection != null)
{
_dataGridCollection.Refresh();
}
}
public bool Filter(object obj)
{
var data = obj as TestClass;
if (data != null)
{
if (!string.IsNullOrEmpty(_filterString))
{
return data.Name.Contains(_filterString) || data.Email.Contains(_filterString);
}
return true;
}
return false;
}
public IEnumerable<TestClass> TestData
{
get
{
yield return new TestClass { Name = "1", Email = "1#test.com" };
yield return new TestClass { Name = "2", Email = "2#test.com" };
yield return new TestClass { Name = "3", Email = "3#test.com" };
yield return new TestClass { Name = "4", Email = "4#test.com" };
yield return new TestClass { Name = "5", Email = "5#test.com" };
yield return new TestClass { Name = "6", Email = "6#test.com" };
yield return new TestClass { Name = "7", Email = "7#test.com" };
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
public class TestClass
{
public string Name { get; set; }
public string Email { get; set; }
}
}
Result: