I have an enum listing all possible settings:
public enum Settings
{
Settings1,
Settings2,
Settings3
}
In my user control i want to implement a new depedency property that holds a list of settings and be able to use it like this:
<my:Control Settings="Settings1, Settings2" />
How should i implement this?
In your UserControl, make your Dependency property a collection of Settings (perhaps rename your enum to Setting), and then you can populate it in XAML with:
<my:Control>
<my:Control.Settings>
<x:Static Member="my:Setting.Setting1" />
<x:Static Member="my:Setting.Setting2" />
</my:Control.Settings>
</my:Control>
I haven't tested this :)
If you want to stick with a comma separated list, then make your UserControl Settings DP a string, and then on the property changed event handler, split the string and use Enum.Parse on each result to store the settings as your Setting enum type.
public class StringDelimitedToEnumListConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
List<Settings> retval = new List<Settings>();
if(value == null || value.GetType() != typeof(string) || targetType != typeof(List<Settings>) )
{
throw new ArgumentException("value must be of type string, target type must be of type List<Settings>");
}
string workingValue = value.ToString();
if (String.IsNullOrEmpty(workingValue))
{
throw new ArgumentException("value must not be an empty string");
}
if (workingValue.Contains(','))
{
string[] tokens = workingValue.Split(',');
foreach (string s in tokens)
{
retval.Add((Settings)Enum.Parse(typeof(Settings), s));
}
return retval;
}
else
{
//there was only 1 value
retval.Add((Settings)Enum.Parse(typeof(Settings),workingValue);
return retval;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
//do we care?
throw new NotImplementedException();
}
}
Related
I would like to write a IMultiValueConverter that converts back value (string) to an array with objects compatible with the types given in targetTypes parameter, as so:
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
if (value != null)
{
List<object> results = new List<object>();
string[] arr = value.ToString().Split(' ');
for (int i = 0; i < arr.Length; i++)
{
object item = (targetTypes[i])arr[i]; //ERROR: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement
}
return results.ToArray();
}
else
{
return null;
}
}
So, for example "London 22" I would like to convert back to "London" (string) and "2" (int). As indicated by the list of types in the targetTypes parameter. And of course, return it as an array.
However, I am not able to cast elements of this array to expected by the targetTypes parameter, as I commented in the code above.
The goal is of course to solve the problem reported during binding: "Can not save the value from target back to source.", Which occurs when an attempt is made to store the string to the int.
Which is reported when I use such a converter:
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
if (value != null)
{
return value.ToString().Split(' ');
}
else
{
return null;
}
}
You may try something like this:
public object[] ConvertBack(
object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
var strings = value.ToString().Split(' ');
if (strings.Length != targetTypes.Length)
{
throw new InvalidOperationException("Invalid number of substrings");
}
return strings
.Select((s, i) => System.Convert.ChangeType(s, targetTypes[i]))
.ToArray();
}
I made custom type which can use in my custom winform control.
I want to display this custom type property in to default winform property window or my custom smart grid.
So i made type converter for my custom type.
[Serializable]
public class TestObj
{
private int a;
private int b;
public int A { get { return a; } set { a = value; } }
public int B { get { return b; } set { b = value; } }
public TestObj()
{
}
public TestObj(int a, int b)
{
this.a = a;
this.b = b;
}
}
// TestObj Converter
public class TestObjConverter : TypeConverter
{
//can convert string -> testobj?
public override bool CanConvertFrom(ITypeDescriptorContext context, >
Type sourceType)
{
if (sourceType == typeof(string))
{
return true;
}
return base.CanConvertFrom(context, sourceType);
}
// string -> TestObj
public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
{
if (value is string)
{
string[] v = ((string)value).Split(new char[] { ' ' });
return new TestObj(Int32.Parse(v[0]), Int32.Parse(v[1]));
}
return base.ConvertFrom(context, culture, value);
}
// TestObj -> string
public override object ConvertTo(ITypeDescriptorContext context,
System.Globalization.CultureInfo culture, object value, Type
destinationType)
{
if (destinationType == typeof(string))
{
return ((TestObj)value).A + " " + ((TestObj)value).B;
}
return base.ConvertTo(context, culture, value, destinationType);
}
}
(If i did not made this typeconverter then resx error will occurred.)
Now i can use TestObj type for my custom control's property.
But i want to use list collection property for this type in my custom control class.
private List<TestObj> testData = new List<TestObj>();
[Category("CustomControl"), Description("Property")]
[TypeConverter(typeof(TestObjConverter))]
public List<TestObj> TestData
{
get { return testData; } set { testData = value; }
}
But i can not use this type's list for property.
If i use this, the collection editor was opened and i can add TestObj element too. But visual studio invoke error [invalid resx file, Culture=newtral, PublicKeyTokrn=null ] when i compile this.
How to use my custom type list in winform property?
But
This may be a little late but someone else may run across this. Your TestObj class that is being used for the property must be declared with the type converter and not the actual property you are exposing. the property is typed with the class as any property should.
[TypeConverter(typeof(TestObjConverter))]
public class TestObj
{
...
}
then in your main control class you just expose the property like normal without the type converter declaration like you have done
[Category("CustomControl"), Description("Property")]
public List<TestObj> TestData { get; set; }
I have several custom controls for winforms if you want a working example of this. Just ask me and i will send one to you
I'm attempting to convert the displayed value of a combobox by using its binding as a key to look for the value I would like to display. I can't seem to get it to work.
The datacontext of my user control is MyObject.
MyObject has a property "MasterDrawerId", which is the Id of "MyReferencedObject".
Elsewhere in my application, accessible through a static property of my App.xaml.cs is a collection of "MyOtherObjects". "MyReferencedObject" has a foreign key relationship with the Id of "MyOtherObject".
My combobox is bound to the "MasterDrawerId", which is what's passed into the converter.
I then use that as a lookup for "MyReferencedObject" to get the foreign key Id of "MyOtherObject" in order to display the name of that object.
I know it seems confusing but it's basically just using the property of the datacontext in order to do a lookup and display the name of another object in its place within a combobox.
This is my code:
masterSiteComboBox.DisplayMemberPath = "Name";
Binding binding = new Binding("MasterDrawerId");
binding.Mode = BindingMode.TwoWay;
binding.Converter = new DrwIdToSiteConverter();
masterSiteComboBox.SelectedItem = binding;
masterSiteComboBox.ItemsSource = ListOfMyOtherObjects;
Here is my converter code:
public class DrwIdToSiteConverter : IValueConverter
{
public DrwIdToSiteConverter()
{
}
public virtual object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
XferSite site = new XferSite();
foreach(XferUserDrawerPermissions perm in App.UserDrawerPermissions)
{
if (perm.DocumentTypeId.Match(value.ToString()))
{
site.Id = int.Parse(perm.SiteId);
site.Name = perm.SiteName;
break;
}
}
return site;
}
public virtual object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value;
}
}
I set a breakpoint at the first line of my "Convert" method of my converter and it never gets hit.
The selected item for a combo box must be an item that is already contained within the collection of objects that you set through the ItemsSource property.
In other words, if your ItemsSource is bound to a collection of Object1, Object2, Object3, you cannot set the SelectedItem to new Object() { Name = 1 }; If you do this, you must override the Equals and GetHashCode methods. This will allow you the ability to set the SelectedItem to a new object.
Example:
public class MyObject
{
public MyObject(string name)
{
if(string.IsNullOrEmpty(name)) throw new ArgumentNullException("name");
Name = name;
}
public string Name { get; private set; }
// override object.Equals
public override bool Equals(object obj)
{
//
// See the full list of guidelines at
// http://go.microsoft.com/fwlink/?LinkID=85237
// and also the guidance for operator== at
// http://go.microsoft.com/fwlink/?LinkId=85238
//
MyObject myObj = obj as MyObject;
if (myObj == null) return false;
return Name == myObj.Name;
}
// override object.GetHashCode
public override int GetHashCode()
{
return Name.GetHashCode;
}
}
var items = new List<MyObject>()
{
new MyObject {Name = "One"},
new MyObject {Name = "Two"},
new MyObject {Name = "Three"},
};
// Converter code
return new MyObject {Name = "One"};
Instead of
masterSiteComboBox.SelectedItem = binding;
do
masterSiteComboBox.SetBinding(ComboBox.SelectedItemProperty, binding);
first: we use infragistics xamdatagrid 11.1.20111.2053
our problem:
We use the grid with generic lists. So it's very dynamic and must be prepared for any situation. We set for each field type theSortComparer, FilterComparer, the editor type, Edita type and style editor.
For some properties of a model, we use special TypeConverter.
For example, in a cell, some values ??can not be displayed.
0 = string.Empty
1 = 1
2 = 2
first solution, we only use the type converter and a special sort comparer:
public class HideZeroIntEntryConverter : Int32Converter
{
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) {
if (value is int) {
if (destinationType == typeof(string)) {
return ((int)value != 0) ? value.ToString() : string.Empty;
}
return ((int)value != 0) ? value : Binding.DoNothing; // this is the best solution to tell the grid the cell is empty
}
return base.ConvertTo(context, culture, value, destinationType);
}
}
this works perfect if we not decide to filter, but if we want to filter the values we see the ugly "Binding.DoNothing" in the filter drop down items (the sorting and filtering is also wrong).
also, we can not filter for "0" because we the converter says string.empty...
second solution, we use a special XamTextEditor:
public class HideZeroIntEntryTextEditor : XamTextEditor
{
public HideZeroIntEntryTextEditor() {
this.ValueToDisplayTextConverter = new HideZeroIntEntryValueConverter();
}
}
public class HideZeroIntEntryValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
if (value is int) {
if (targetType == typeof(string)) {
return ((int)value != 0) ? value.ToString() : string.Empty;
}
// this never happens
return ((int)value != 0) ? value : Binding.DoNothing;
}
// this never happens
return targetType == typeof(string) && value != null ? value.ToString() : value;
}
}
and for the field settings
field.Settings.EditAsType = typeof(int);
field.Converter = null;
field.Settings.EditorType = typeof(HideZeroIntEntryTextEditor);
field.Settings.SortComparer = GenericComparer<int>.Instance;
field.Settings.FilterComparer = GenericComparer<int>.Instance;
field.Settings.GroupByComparer = GroupByRecordComparer<int>.Instance;
Now we can filter to "0", even if this does not appear in the list.
But, in both cases, we can not filter by empty entries, because it actually does not exist!
We want to though!
In our opinion, this could be if we could make our own special filter. But this is unfortunately not so easy.
Yes, we can remove the special filter blanks and NonBlanks, BUT that applies to all grids.
A special filter to override is very complicated and does not even correct.
Ok, we want see empty cell, and want filter this cells, but the special filter doesn't works correct!
What can we do, any ideas?
here is the question at infragistics
I answered this on the Infragistics forums right here with a sample.
You can use the RecordFilterDropDownPopulating event to remove the existing FilterDropDownItem for "(Blanks)" and replace it with a custom one for the value of 0 when the editor for the field is HideZeroIntEntryTextEditor. The following code accomplishes this:
void XamDataGrid1_RecordFilterDropDownPopulating(
object sender,
//Infragistics.Windows.DataPresenter.Events.
RecordFilterDropDownPopulatingEventArgs e)
{
if (e.Field.EditorTypeResolved == typeof(HideZeroIntEntryTextEditor)) {
bool found = false;
int index = 0;
while (index < e.DropDownItems.Count && !found) {
FilterDropDownItem item = e.DropDownItems[index];
if (item.DisplayText == "(Blanks)") {
e.DropDownItems.Remove(item);
e.DropDownItems.Insert(index, new FilterDropDownItem(
new ComparisonCondition(ComparisonOperator.Equals, 0), "(Blanks)"));
found = true;
}
index++;
}
}
}
I have some properties which implement the INotifyPropertyChanged interface. It works fine. But in my code I also use some value converters (if value < 3 - make grid red, if value >3 and value < 10 - make grid blue, etc.).
The problem is how to refresh value converter after PropertyChanged was raised?
Is there simple code behind solution?
Thanks all and sorry for my bad English!
Here some code:
public class NotifyColors : INotifyPropertyChanged
{
private Color _TodayColor;
public Color TodayColor
{
get
{
return _TodayColor;
}
set
{
if (_TodayColor != value)
{
_TodayColor = value;
OnPropertyChanged("TodayColor");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
// it raised correctly when I change color with color picker control
}
}
}
// here is value converter
[ValueConversion(typeof(object), typeof(Brush))]
public class PositionToBackgroundConverter : IValueConverter
{
ModulePreferences ModulePrefs;
public PositionToBackgroundConverter(ModulePreferences ModulePrefs)
{
this.ModulePrefs = ModulePrefs;
}
#region IValueConverter Member
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (ModulePrefs.UseColoringByPosition)
{
try
{
if (value != null)
{
short value_short = (short)value;
if (value_short <= 3)
return (Brush)new SolidColorBrush(ModulePrefs.NotifyColorsObj._TodayColor); // here is changing property
else
return (Brush)new SolidColorBrush(ModulePrefs.NotifyColorsObj.T100PlusColor);
}
else
return Brushes.Transparent;
}
catch
{
return Brushes.Transparent;
}
}
else
return Brushes.Transparent;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
#endregion
}
And here I apply my value converter to the grid:
// assign backgroundconverter
var grid = new FrameworkElementFactory(typeof(Grid));
bin = new Binding();
bin.Path = new PropertyPath(string.Format("DataItem.{0}", LastPositionColumnName));
bin.Converter = new PositionToBackgroundConverter(ProjectViewObj.ModulePrefs);
grid.SetValue(Grid.BackgroundProperty, bin);
If you have done it "correctly" the PropertyChanged event will cause an update of the bindings which bind to that property, when this occurs any converters that are used in the same binding will reconvert the values. So normally the conversions happen on their own. If this is not the case you are probably using the converters "inappropriately", please post some code because without it it's quite impossible to tell what exactly you are doing wrong.
Edit: You use grid.SetValue(Grid.BackgroundProperty, bin), you should use grid.SetBinding(Grid.BackgroundProperty, bin) instead since it is a binding.
Edit2: This really has nothing to do with converters.
In your sample code you bind to IntValue, then you change TodayColor and expect the binding to be updated, not gonna happen. If you want the binding to react to both properties you have to either use a MultiBinding or raise the respective events since your properties are interdependent.
i.e.
private Color _TodayColor;
public short _IntValue;
public short IntValue
{
get { return _IntValue; }
set
{
if (_IntValue != value)
{
_IntValue = value;
OnPropertyChanged("IntValue");
OnPropertyChanged("TodayColor");
}
}
}
public Color TodayColor
{
get { return _TodayColor; }
set
{
if (_TodayColor != value)
{
_TodayColor = value;
OnPropertyChanged("TodayColor");
OnPropertyChanged("IntValue");
}
}
}