How can i get the value of the custom property? - c#

I was looking for a way to add a custom property to a xaml control. I found this solution: Adding custom attributes to an element in XAML?
Class1.cs:
public static Class1
{
public static readonly DependencyProperty IsTestProperty =
DependencyProperty.RegisterAttached("IsTest",
typeof(bool),
typeof(Class1),
new FrameworkPropertyMetadata(false));
public static bool GetIsTestProperty(UIElement element)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
return (bool)element.GetValue(IsTestProperty);
}
public static void SetIsTestProperty(UIElement element, bool value)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
element.SetValue(IsTestProperty, value);
}
}
UserControl.xaml
<StackPanel x:Name="Container">
<ComboBox x:Name="cfg_Test" local:Class1.IsTest="True" />
<ComboBox x:Name="cfg_Test" local:Class1.IsTest="False" />
...
...
Now is my question, how can i get the value of the property?
Now I want to read the value of all elements, in the StackPanel.
// get all elementes in the stackpanel
foreach (FrameworkElement child in
Helpers.FindVisualChildren<FrameworkElement>(control, true))
{
if(child.GetValue(Class1.IsTest))
{
//
}
}
but child.GetValue(Class1.IsTest) is always false... what's wrong?

First of all, it seems, that your code is full of errors, so iam not sure, if you did not copy it properly, or whats the reason for that.
So whats wrong in your example?
The getter and setter of your DependencyProperty are wrongly created. (There should be no "Property" attached to the names.) It chould be:
public static bool GetIsTest(UIElement element)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
return (bool)element.GetValue(IsTestProperty);
}
public static void SetIsTest(UIElement element, bool value)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
element.SetValue(IsTestProperty, value);
}
Secondly, both of your child controls of the StackPanel share the same name, thats not possible too.
And third, you getting the property in your foreach-statement wrongly. This should be:
if ((bool)child.GetValue(Class1.IsTestProperty))
{
// ...
}
Be sure, that your Helpers.FindVisualChildren is working correctly. You could use the following instead:
foreach (FrameworkElement child in Container.Children)
{
// ...
}
Hope this helps.

Related

Shorten syntax for nearly identical properties

I have to rework some code and stumbled upon a few classes which define a huge amount of very similar properties.
They look something like this:
public _ReturnType _PropertyName
{
get
{
IMarkerInterface value = null;
if (Properties != null) Properties.TryGetValue(_string, out value);
return value as _ReturnType;
}
set { Properties[_string] = value; }
}
The only difference between them is the _ReturnType, the _string that is used in the dictionary Properties and obviously the _PropertyName.
I was wondering if there is a way to shorten the syntax?
If you see duplicate code, you extract a method. It would look something like this:
private T GetValueOrDefault<T>(string key)
{
IMarkerInterface value = null;
if (Properties != null) Properties.TryGetValue(key, out value);
return value as T;
}
Then change your getter:
get
{
return GetValueOrDefault<_ReturnType>("key");
}
But if this code is spread out over multiple classes, you'd have to define a base class containing the Properties property and the above GetValueOrDefault() method, albeit protected instead of private.
Alternatively, you'd define it as an extension method on whatever the type of Properties is:
public static T GetValueOrDefault<T>(this IDictionary<string, IMarkerInterface> properties, string key)
{
IMarkerInterface value = null;
if (properties != null) properties.TryGetValue(key, out value);
return value as T;
}
And call it as such:
get
{
return Properties.GetValueOrDefault<_ReturnType>("key");
}
But, as #Daniel comments, this smells like an ideal scenario for code generation, because without that you'd still have a couple of lines of (copy-pasted, error-prone) code.
There probably is a source somewhere for what these properties should be named, and you can use something like T4 templates to generate this code file from it.
Well, you could do this:
private IMarkerInterface getIMF(string str)
{
IMarkerInterface value = null;
Properties?.TryGetValue(_string, out value);
return value;
}
public _ReturnType _PropertyName
{
get { return getIMF(_string) as _ReturnType; }
set { Properties[_string] = value; }
}
If Properties implements IReadOnlyDictionary<string, object> (like a Dictionary<string, object> for instance), one thing you could do is add an extension method:
public static TValue TryGetValue<TValue>(
this IReadOnlyDictionary<string, object> properties,
string key)
where TValue : class
{
if ((properties != null) &&
properties.TryGetValue(key, out object value))
{
return value as TValue;
}
return null;
}
and then
public IMarkerInterface MarkerInterface
{
get => Properties.TryGetValue<IMarkerInterface>("MarkerInterface");
set { Properties["MarkerInterface"] = value; }
}
Link to Fiddle

Is there another way to code a loop that selects from one of many with LINQ?

I have numerous examples of code like this in my application:
foreach (var setting in vm.PTI)
{
if (setting.Name == name)
{
setting.IsSelected = true;
App.DB.UpdateIntSetting(SET.Pti, setting.Id);
}
else
setting.IsSelected = false;
}
Loops that run through an array, pick the one row that matches and then set properties in the array.
Although this works well, I am wondering if there are other ways to write this code using LINQ or other. I suspect this might be the best ways but would be interested to get feedback from others as this is something I do maybe 20 times in my code and if there's a better way to do it I would like to try using the better way.
Here is the class that is used:
ParamViewModel[] _pti;
public ParamViewModel[] PTI
{
get => _pti;
set => SetProperty(ref _pti, value);
}
public class ParamViewModel: BaseViewModel
{
int _id;
public int Id
{
get => _id;
set => SetProperty(ref _id, value);
}
string _name;
public string Name
{
get => _name;
set => SetProperty(ref _name, value);
}
bool _isSelected;
public bool IsSelected
{
get => _isSelected;
set => SetProperty(ref _isSelected, value);
}
}
For your particular case you can write extension method:
public static void ModifyCollection<T>(this IEnumerable<T> source, Predicate<T> selectedPredicate,Action<T> selectedAction, Action<T> othersAction=null)
{
if (selectedPredicate == null) throw new ArgumentNullException(nameof(selectedPredicate));
if (selectedAction == null) throw new ArgumentNullException(nameof(selectedAction));
foreach (var element in source)
{
if (selectedPredicate(element))
{
selectedAction(element);
}
else
{
othersAction?.Invoke(element);
}
}
}
How to use it:
PTI.ModifyCollection(
selectedPredicate:(s) => s.Name == name,
selectedAction:(s) =>
{
s.IsSelected = true;
App.DB.UpdateIntSetting(SET.Pti, setting.Id);
},
othersAction:(s) => s.IsSelected = false);
You can go further and specify constraints to T. 'where T is INamed' and put predicate code inside extension method.
You can write your own extension method:
public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
{
if (source == null) throw new ArgumentNullException("source");
if (action == null) throw new ArgumentNullException("action");
foreach (var element in source)
{
action(element);
}
}
You can get the setting first with LINQ, then set it.
var setting = vm.PTI.FirstOrDefault( s => s.Name == name );
if (setting != null)
{
setting.IsSelected = true;
App.DBUpdateIntSetting(SET.Pti, setting.Id);
}

Iterating through a Tree and getting certain children in C#

I'm currently working with a tree structure defined like this
public class TreeNode
{
private ObservableCollection<TreeItem> nodeItems;
private ObservableCollection<TreeNode> nodeChildren;
//public "NodeItems" and "NodeChildren" getters and setters
}
public class TreeItem
{
private bool isSelected;
private string Name;
//public "IsSelected" and "Name" getters and setters
}
public class Tree
{
private TreeNode rootNode;
//public getters and setters properties
}
and I'm trying to write a function or a public property that recursively gets all the nodeItems in the Tree that have isSelected == true and make it a flat collection.
So I wrote this function in the TreeNode class, that recursively navigates through the children:
public ObservableCollection<TreeItem> SelectedItems()
{
ObservableCollection<TreeItem> tempCollection = new ObservableCollection<TreeItem>();
if (nodeItems != null)
{
foreach (TreeItem item in nodeItems)
{
if (item.IsSelected == true)
{
tempCollection.Add(item);
}
}
}
if (nodeChildren != null)
{
foreach (TreeNode node in nodeChildren)
{
tempCollection.Concat(node.SelectedItem());
}
}
return tempCollection;
}
but it always returns an empty collection at the end.
How can I correct it, and maybe improve it (by using a Lambda expression or a property)?
The Concat function on the ObservableCollection does not modify any of the arguments. You have to assign the resulting object to your tempCollection.
if (nodeChildren != null)
{
foreach (TreeNode node in nodeChildren)
{
tempCollection = new ObservableCollection<TreeNode>(tempCollection.Concat(node.SelectedItem()));
}
}
EDIT: Alternatively, you can use an overloaded private method to not use so many temporary collections:
public ObservableCollection<TreeItem> SelectedItems()
{
ObservableCollection<TreeItem> toReturn = new ObservableCollection<TreeItem>();
SelectedItems(toReturn);
return toReturn;
}
private void SelectedItems(ObservableCollection<TreeItem> tempCollection)
{
if (nodeItems != null)
{
foreach (TreeItem item in nodeItems)
{
if (item.IsSelected == true)
{
tempCollection.Add(item);
}
}
}
if (nodeChildren != null)
{
foreach (TreeNode node in nodeChildren)
{
node.SelectedItems(tempCollection);
}
}
}
You can simplify your definition of a tree down to this:
public class Tree : ObservableCollection<Tree>
{
public ObservableCollection<TreeItem> nodeItems;
}
Now you can do this:
public IEnumerable<TreeItem> FlattenIsSelected(Tree tree)
{
return tree.nodeItems.Where(x => x.isSelected)
.Concat(tree.SelectMany(t => FlattenIsSelected(t)));
}
It's not much more difficult if you keep your current definitions.

Instantiating a class based on a property set value

When presenting the user with a ComboBox with ObservableCollection<Type> as its ItemsSource, how can I instantiate a class in the property that SelectedItem is bound to?
The elements in the ElementList list in parentItem is either of a generic class type Element, or is of a type inheriting from Element (e.g. DigitalOutputButton or TrendGraph).
XAML:
<StackPanel Orientation="Horizontal">
<TextBlock Width="100" Text="Element Type:" />
<ComboBox Width="300" ItemsSource="{Binding Path=Element.ElementTypeList}"
SelectedItem="{Binding Path=Element.SelectedElementType}" />
</StackPanel>
C# code:
private static ObservableCollection<Type> _elementTypeList
= new ObservableCollection<Type> { typeof(Element), typeof(DigitalOutputButton), typeof(TrendGraph) };
public static ObservableCollection<Type> ElementTypeList { get { return _elementTypeList; } }
public Type SelectedElementType {
get { return GetType(); }
set {
if (value != GetType()) {
var parentItem = Controller.ConfigurationHandler.FindParentItem(this);
var currentItemIndex = parentItem.ElementList.IndexOf(this);
parentItem.ElementList[currentItemIndex] = new typeof(value)();
}
}
}
The above set code will not build. But is it possible to achieve this behavior in another way?
EDIT: Ok, this way works:
public Type SelectedElementType {
get { return GetType(); }
set {
if (value != GetType()) {
var parentItem = Controller.ConfigurationHandler.FindParentItem(this);
var currentItemIndex = parentItem.ElementList.IndexOf(this);
if (value == typeof(Element)) {
parentItem.ElementList[currentItemIndex] = new Element();
}
else if (value == typeof(DigitalOutputButton)) {
parentItem.ElementList[currentItemIndex] = new DigitalOutputButton();
}
else if (value == typeof(TrendGraph)) {
parentItem.ElementList[currentItemIndex] = new TrendGraph();
}
}
}
}
But it would be great it there was a way to do this that were a little more "maintenance free" (no need to edit when adding a new element type).
var instance = Activator.CreateInstance(value, Controller, ParentElementGroup, ItemLabel);
parentItem.ElementList[currentItemIndex] = (TYPE)instance;
The only missing link is the type of your collection so it can be cast. But that should be compile time knowledge.

Is it possible to create generic class which only accepts a type which has a certain property

I want to create a class "Indicator" that will accept 'Control' and set its 'Image' property.
Since Control doesn't have an Image property, I want to implement a template class ("Indicator") which will only accept classes which have this property (Image).
Is it possible?
We could change the way how an instance of this class is created by adding a private parameterless constructor and another public ctor in which we'll do our type checks:
class Indicator<T> where T : Control
{
private T _control;
private Indicator()
{
}
public Indicator(T control)
{
if(control.GetType().GetProperties().All(p => p.Name != "Image" || p.PropertyType != typeof(Image)))
{
throw new ArgumentException("This type of control is not supported");
}
this._control = control;
}
}
You can use reflection to get the property of your object:
public class ImagePropertyModifier
{
private PropertyInfo GetImageProperty(object obj)
{
var property = obj.GetType().GetProperty("Image");
if (property == null)
throw new Exception("Object has no Image property.");
if (property.PropertyType != typeof(string))
throw new Exception("Object's Image property is not a string.");
return property;
}
private static string GetImage(object obj)
{
return GetImageProperty(obj).GetValue(obj, null).ToString();
}
private static string SetImage(object obj, string value)
{
GetImageProperty(obj).SetValue(obj, value);
}
}
Note that this code assumes that Image is a string (path to an image). You can change the type in accordance with your requirements. It is just an example.
Building on #Fabjan's answer, instead of using reflection to perform the type inspection required to identify the Image property every time you instantiate instances of the object, you can leverage static constructors to perform that check only when the type itself is instantiated:
class Indicator<T> where T : Control
{
static Indicator()
{
{
if (typeof(T).GetProperties().All(p => p.Name != "Image" || p.PropertyType != typeof(Image)))
throw new Exception(typeof(T).Name + " is not a supported generic type argument because it does not have an Image property");
}
}
private readonly T _control;
public Indicator(T control)
{
_control = control;
}
}

Categories

Resources