How to Get the "Content" of any Control in WPF? - c#

I am coding a function which will take a Control Ctrl as Arguement and Modify the Control.Content of it.
Is there Any way to Get and Set the Content of any Control??
Code :
void RemoveHotKey(Control Ctrl, int KeyIndex)
{
if (Ctrl.Content.ToString().Substring(KeyIndex, 1) == "_") // System.Windows.Controls.Control does not contain a definition for 'Content'
{
Ctrl.Content = Ctrl.Content.ToString().Remove(KeyIndex, 1); // System.Windows.Controls.Control does not contain a definition for 'Content'
}
}

try this instead:
void RemoveHotKey(ContentControl Ctrl, int KeyIndex)
{
if (Ctrl.Content.ToString().Substring(KeyIndex, 1) == "_")
{
Ctrl.Content = Ctrl.Content.ToString().Remove(KeyIndex, 1);
}
}
take a look here.
or this:
void RemoveHotKey(Control Ctrl, int KeyIndex)
{
ContentControl contentCtrl = Ctrl as ContentControl;
if (contentCtrl != null && contentCtrl.Content != null)
{
if (contentCtrl.Content.ToString().Substring(KeyIndex, 1) == "_")
{
contentCtrl.Content = contentCtrl.Content.ToString().Remove(KeyIndex, 1);
}
}
}
which is way less expensive than using reflection..

You could change the signature of your method to this:
void RemoveHotKey(ContentControl Ctrl, int KeyIndex)
a ContentControl always has a Content property.

You could use reflection to check whether Control in fact has a Content property...
Type t = Ctrl.GetType();
PropertyInfo p = t.GetProperty("Content");
if (p != null)
{
string val = p.GetValue(Ctrl, null) ?? "";
val = val.Replace("_", "");
p.SetValue(Ctrl, val, null);
}

Related

copy properties c# but check if a property is already set

I working hard on a JSON-Deserializer on my own. Just for training. I am nearly finished, but I have a copying issue.
I already have this:
public void CopyValues<T>(T target, T source)
{
Type t = typeof(T);
var properties = t.GetProperties().Where(prop => prop.CanRead && prop.CanWrite);
foreach (var prop in properties)
{
var value = prop.GetValue(source, null);
if (value != null)
prop.SetValue(target, value, null);
}
}
The Main Problem is here. I have a Property containing 2 Properties. Like content.link and content.value.
If I use the Copy Function it copys right. No discussion. But if I put the copy function into a loop, and the data is filled up, the source having also
"content", but without link and value.
If I copy again, the already correctly filled properties are getting overridden, and the result is that I have only null at conent.link and content.value.
Is there a way, to check if link and value is set to null ?
In order to copy the nested properties, you will need use a recursive function:
public static void DeepCopy<T>(T target, T source)
{
DeepCloneImpl(typeof(T), source, target);
}
public static T DeepClone<T>(T template)
where T : new()
{
return (T)DeepCloneImpl(typeof(T), template);
}
private static object DeepCloneImpl(Type type, object template, object stump = null)
{
if (template == null)
{
return null;
}
var clone = stump ?? Activator.CreateInstance(type);
var clonableProperties = type.GetProperties()
.Where(x => x.GetMethod != null && x.SetMethod != null);
foreach (var property in clonableProperties)
{
var propertyType = property.PropertyType;
if (propertyType.GetTypeInfo().IsValueType || propertyType == typeof(string))
{
var value = property.GetValue(template);
property.SetValue(clone, value);
}
else if (propertyType.GetTypeInfo().IsClass && propertyType.GetConstructor(Type.EmptyTypes) != null)
{
var value = DeepCloneImpl(propertyType, property.GetValue(template));
property.SetValue(clone, value);
}
else if (propertyType.IsArray)
{
var source = property.GetValue(template) as Array;
if (source == null)
{
continue;
}
var elementType = propertyType.GetElementType();
if (elementType.GetTypeInfo().IsValueType || elementType == typeof(string))
{
var copies = Array.CreateInstance(elementType, source.Length);
Array.Copy(source, copies, source.Length);
property.SetValue(clone, copies);
}
else if (elementType.GetTypeInfo().IsClass)
{
var copies = Array.CreateInstance(elementType, source.Length);
for (int i = 0; i < source.Length; i++)
{
var copy = DeepCloneImpl(elementType, source.GetValue(i));
copies.SetValue(copy, i);
}
property.SetValue(clone, copies);
}
}
}
return clone;
}
This should cover most of the use case, however you will have to handle self/circular references.
From what I can gather, the problem is values are being overridden. And from looking at your method, it appears to be doing what you'd expect, it's a soft clone of property values. Depending on what a correctly filled property is in your situation, you'll have to make a comparison that prevents the property from being set again. Perhaps value != null should actually be value == null so that you're only setting the value when there wasn't already one. Because of the generic typing of this method you don't have to worry about property mismatches if that's why you're using != null. Both arguments, source and target, will be of the same type.

WPF validation run-time generated forms

I'm creating a WPF application which generates a form based on a model to edit it. I use reflection to go through all properties of the model to create inputfields for the properties. The GenerateForm method iterates through the properties and uses the SimpleInputFactory to generate input fields. I want to validate the input of the generated fields, but all validation methods require that you know what you are going to validate (either it's using generics or you have to specify it on the binding in the XAML). I want to validate the input based on attributes in the models. Is there any existing way of doing this? I could just make it myself, but if there is some existing way it would help.
Thanks in advance.
public static Grid GenerateForm(List<object> basisgegevensModels, AddOrEdit addOrEdit)
{
if (basisgegevensModels.Count <= 0)
return null;
Grid formGrid = new Grid();
formGrid.Margin = new Thickness(20,20,20,20);
formGrid.HorizontalAlignment = HorizontalAlignment.Stretch;
AddColumnToGrid(formGrid, GridUnitType.Star, 1);
AddColumnToGrid(formGrid, GridUnitType.Star, 3);
AddColumnToGrid(formGrid, GridUnitType.Star, 1);
AddColumnToGrid(formGrid, GridUnitType.Star, 3);
AddRowToGrid(formGrid, GridUnitType.Auto, 0);
var propertyInfos = new List<PropertyInfo>();
foreach (var propertyInfo in basisgegevensModels[0].GetType().GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance))
{
var visibleAttribute = propertyInfo.GetCustomAttributes(typeof(Visible), false).Cast<Visible>().FirstOrDefault();
if (visibleAttribute == null || visibleAttribute.IsVisible)
propertyInfos.Add(propertyInfo);
}
int column = 0;
int row = 0;
foreach (var property in propertyInfos)
{
if (row >= Math.Ceiling((decimal)propertyInfos.Count / 2) && row != 0 && column != 2)
{
column = 2;
row = 0;
}
var displayNameAttribute = basisgegevensModels[0].GetType().GetProperty(property.Name).GetCustomAttributes(typeof(DisplayNameAttribute), false)
.Cast<DisplayNameAttribute>().FirstOrDefault();
string displayName;
if (displayNameAttribute != null)
displayName = displayNameAttribute.DisplayName;
else
displayName = property.Name;
bool isEditAllowed = true;
if (addOrEdit == AddOrEdit.Edit)
{
var editAllowed =
basisgegevensModels[0].GetType()
.GetProperty(property.Name)
.GetCustomAttributes(typeof (EditAllowed), false)
.Cast<EditAllowed>()
.FirstOrDefault();
if (editAllowed != null)
isEditAllowed = editAllowed.IsEditAllowed;
}
//add label for inputfield
TextBlock label = SimpleInputFieldFactory.CreateTextBlock(displayName, column, row);
label.VerticalAlignment = VerticalAlignment.Center;
formGrid.Children.Add(label);
column++;
//add input field
formGrid.Children.Add(SimpleInputFieldFactory.CreateInputField(basisgegevensModels, property, isEditAllowed, column, row, 300, HorizontalAlignment.Left));
column--;
row++;
if (column == 0)
{
AddRowToGrid(formGrid, GridUnitType.Auto, 0);
}
}
return formGrid;
}
SimpleInputFieldFactory Class:
public class SimpleInputFieldFactory
{
public static Control CreateInputField(List<object> basisgegevensModels, PropertyInfo property, bool editAllowed, int column, int row, double inputFieldWidth, HorizontalAlignment inputFieldHorAlignment)
{
Control inputField = null;
var triggers = new List<System.Windows.Interactivity.EventTrigger>();
var multiBinding = new MultiBinding();
multiBinding.NotifyOnSourceUpdated = true;
multiBinding.Mode = BindingMode.TwoWay;
multiBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
foreach (var basisgegevensModel in basisgegevensModels)
{
Binding binding = new Binding(property.Name)
{
UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged,
Source = basisgegevensModel,
Mode = BindingMode.TwoWay
};
multiBinding.Bindings.Add(binding);
}
//add inputfield
if (property.PropertyType == typeof(string) || property.PropertyType == typeof(int))
{
string valueAsString = "";
if (property.GetValue(basisgegevensModels[0]) != null)
valueAsString = property.GetValue(basisgegevensModels[0]).ToString();
inputField = CreateTextBox(valueAsString, column, row);
triggers.Add(new System.Windows.Interactivity.EventTrigger("EditValueChanged"));
}
else if (property.PropertyType == typeof(bool))
{
bool valueAsBool = false;
if (property.GetValue(basisgegevensModels[0]) != null)
valueAsBool = (bool)property.GetValue(basisgegevensModels[0]);
inputField = CreateCheckBox(valueAsBool, column, row);
triggers.Add(new System.Windows.Interactivity.EventTrigger("EditValueChanged"));
}
else if (property.PropertyType.BaseType == typeof(Enum))
{
int valueAsInt = 0;
if (property.GetValue(basisgegevensModels[0]) != null)
valueAsInt = (int)property.GetValue(basisgegevensModels[0]);
inputField = CreateDropDown(property.PropertyType, valueAsInt, column, row);
triggers.Add(new System.Windows.Interactivity.EventTrigger("EditValueChanged"));
((ComboBoxEdit)inputField).SelectedIndex = valueAsInt;
((ComboBoxEdit)inputField).IsTextEditable = false;
}
//add general settings, bindings and triggers
if (inputField != null)
{
inputField.Width = inputFieldWidth;
inputField.HorizontalAlignment = inputFieldHorAlignment;
inputField.Margin = new Thickness(5);
inputField.IsEnabled = editAllowed;
var multiEditAllowedAttribute = property.GetCustomAttributes(typeof(MultiEditAllowed), false)
.Cast<MultiEditAllowed>().FirstOrDefault();
//only add binding and trigger if 1 entity is selected OR multiedit is allowed
if (basisgegevensModels.Count == 1 || multiEditAllowedAttribute == null || multiEditAllowedAttribute.IsMultiEditAllowed)
{
multiBinding.Converter = new MultiEditValueConverter();
inputField.SetBinding(BaseEdit.EditValueProperty, multiBinding);
foreach (var trigger in triggers)
{
var action = new ActionMessage();
action.MethodName = "InputChanged";
trigger.Actions.Add(action);
Interaction.GetTriggers(inputField).Add(trigger);
}
}
else
{
inputField.IsEnabled = false;
}
return inputField;
}
return null;
}
public static List<string> GetEnumList(Type enumType)
{
if (!enumType.IsEnum)
{
return new List<string>();
}
return Enum.GetNames(enumType).ToList();
}
public static TextBlock CreateTextBlock(string text, int column, int row)
{
TextBlock textBlock = new TextBlock();
textBlock.Text = text;
Grid.SetColumn(textBlock, column);
Grid.SetRow(textBlock, row);
return textBlock;
}
private static TextEditBase CreateTextBox(string text, int column, int row)
{
TextEdit textBox = new TextEdit();
textBox.Text = text;
Grid.SetColumn(textBox, column);
Grid.SetRow(textBox, row);
return textBox;
}
private static CheckEdit CreateCheckBox(bool isChecked, int column, int row)
{
CheckEdit checkBox = new CheckEdit();
checkBox.IsChecked = isChecked;
Grid.SetColumn(checkBox, column);
Grid.SetRow(checkBox, row);
return checkBox;
}
private static ComboBoxEdit CreateDropDown(Type enumType, int value, int column, int row)
{
ComboBoxEdit dropDown = new ComboBoxEdit();
foreach (var enumValue in GetEnumList(enumType))
{
dropDown.Items.Add(enumValue);
}
dropDown.SelectedIndex = value;
Grid.SetColumn(dropDown, column);
Grid.SetRow(dropDown, row);
return dropDown;
}
}
Yes, you can use the System.ComponentModel.DataAnnotations for validation.
Documentation for the base namespace : MSDN: System.ComponentModel.DataAnnotations
Examples include RequiredAttribute and RangeAttribute.
Microsoft also provide an excellent example of how to provide validation feedback in realtime to the user in WPF using the ErrorTemplate and Binding in the following example: MSDN: Validation in MVVM using Data Annotations
I've also developed a small framework for my own purposes which incorporates these techniques - basically a base class where you need to decorate your VM with ValidationAttribute derived attributes and use the appropriate Binding and WPF takes care of the rest. GitHub: ValidatingBaseViewModel

What is this ArgumentOutOfRangeException caused by?

I have a problem that drives me nuts. I am using a generic List that throws an ArgumentOutOfRangeException whenever I try to assign its first (or last?) index to a variable. It's a pretty large pile of code, hence I'll try to extract only the relevant stuff. So here it is:
private string GetRuleByName(string name, List<string> rules)
{
if(rules != null)
{
List<string> todo = new List<string>();
todo.AddRange(rules);
while(rules.Count != 0)
{
string r = todo[0]; // <- Error 'ArgumentOutOfRangeException' here
todo.RemoveAt(0);
// ...
}
}
}
That's how I call the method:
void treeView_AfterSelect(object sender, TreeViewEventArgs e)
{
string currentRule = GetRuleByName(treeView.SelectedNode.FullPath, ruleCollection)
// the string list "ruleCollection" always contains
// strings and thus is never empty
}
Even though it's not a very detailed presentation of what's going on, as I had to cut off a piece of some complicated code, I really hope someone else might see what produces the error.
Big thanks in advance for at least having a look!
EDIT:
This is how the method looks like. I haven't altered anything, to show what's really inside it. I hope it makes sense for somebody:
private Rule GetRuleByNameOrId(string stName, List<Rule> rules)
{
if(rules != null)
{
string searchName = stName.ToLower().Trim();
string subName = "";
int slashPos = searchName.IndexOf('/');
if(slashPos != -1)
{
if(slashPos != searchName.Length)
subName = searchName.Substring(slashPos + 1);
searchName = searchName.Substring(0, slashPos);
}
List<Rule> todo = new List<Rule>();
todo.AddRange(rules);
while(todo.Count != 0)
{
Rule r = (Rule)todo[0];
todo.RemoveAt(0);
if(r.Name.ToLower() == searchName || r.Id.ToLower() == searchName)
{
if(subName != "")
{
Rule subRule = GetRuleByNameOrId(subName, r.Children);
if(subRule != null)
return subRule;
}
else
{
return r;
}
}
if(r.Children != null && r.Children.Count != 0)
todo.AddRange(r.Children);
}//end while
}//end if(rules != null)
return null;
}
Sounds like you want the following:
private string GetRuleByName(string name, List<string> rules)
{
if(rules != null)
{
List<string> todo = new List<string>();
todo.AddRange(rules);
while(todo.Count != 0) // <-- Minor mod here
{
string r = todo[0];
todo.RemoveAt(0);
// ...
}
}
}
Otherwise you are infinitely looping on rules.Count as the size of rules is not changing
This works fine until todo is empty, then you get the exception because element 0 no longer exists as you've removed them all!
Are you sure you don't want this instead:
while(rules.Count != 0)
{
string r = rules[0]; // <- Error 'ArgumentOutOfRangeException' here
rules.RemoveAt(0);
?
The way you've written it now, you've got a loop that would actually be infinite, except that you eventually remove all the elements from the todo list, at which point you throw the exception (because in an empty list even the 0-th element doesn't exist).

get all control from multiple form in csharp

I want to get all the control from multiple form (Main, Two and Three) and
compare if the control tag equals the variable str_name and if true write the
value of str_value in c.Text.
the code:
private static Form[] getformular()
{
Main main = new Main();
Two f2 = new Two();
Three f3 = new Three();
Form[] form = { main, f2, f3};
return form;
}
private void initcontrol()
{
String str_name = "name";
String str_value = "value";
foreach(Form f in getformular())
{
foreach (Control c in f.Controls)
{
if (f != null && c = null)
{
if (c.Tag.Equals(str_name))
{
c.Text = str_value;
}
}
}
}
}
Could please someone help me?
First, as stated by #JonB some of conditional checking (ifs logic) in your current code seems off.
Second, looping through Form.Controls will only bring you all controls placed directly in the Form. For example if you have tab control (or any other container control) placed in form, and you have a textbox inside that tab control, you'll get only the tab control and couldn't find the textbox by looping through Form.Controls. You can solve that with recursive method as demonstrated below.
private void initcontrol()
{
String str_name = "name";
String str_value = "value";
var result = false;
foreach(Form f in getformular())
{
//if you want to check if f null, it should be here.
if(f != null) result = setControlText(f, str_name, str_value);
}
if(!result) MessageBox.Show("Control not found");
}
private bool setControlText(Control control, string str_name, string str_value)
{
var isSuccess = false;
foreach (Control c in control.Controls)
{
//if c is not null
if (c != null)
{
//check c's Tag, if not null and matched str_name set the text
if (c.Tag != null && c.Tag.Equals(str_name))
{
c.Text = str_value;
isSuccess = true;
}
//else, search in c.Controls
else isSuccess = setControlText(c, str_name, str_value);
//if control already found and set, exit the method now
if (isSuccess) return true;
}
}
return isSuccess;
}

How To Find Checked Radio Button Using Group name My code Attached?

i am trying to find the value of checked radio using group name i have method that return it but what should i pass in that method along with group name
method is here,
private string getRadioValue(ControlCollection clts, string groupName)
{
string ret = "";
foreach (Control ctl in clts)
{
if (ctl.Controls.Count != 0)
{
if (ret == "")
ret = getRadioValue(ctl.Controls, groupName);
}
if (ctl.ToString() == "System.Web.UI.WebControls.RadioButton")
{
RadioButton rb = (RadioButton)ctl;
if (rb.GroupName == groupName && rb.Checked == true)
ret = rb.Attributes["Value"];
}
}
return ret;
}
i use it like
Oc.aProjectSubmited = getRadioValue(RadioButton,"Aps");
where Aps is radio group but getting error "invalid argument" on radio button i pass ??
hopes for your suggestion thanks in advance
This is because you are passing RadioButton. Your method accepts ControlCollection, not a Control.
Why not pass this.Controls to pass the whole ControlCollection of the page? Or any other ControlCollection that you might be using to keep that RadioButton you want to check?
Here's an example:
protected void Page_Load(object sender, EventArgs e)
{
getRadioValue(this.Controls, "Hello");
}
private string getRadioValue(ControlCollection clts, string groupName)
{
string ret = "";
foreach (Control ctl in clts)
{
if (ctl.Controls.Count != 0)
{
if (ret == "")
ret = getRadioValue(ctl.Controls, groupName);
}
if (ctl.ToString() == "System.Web.UI.WebControls.RadioButton")
{
RadioButton rb = (RadioButton)ctl;
if (rb.GroupName == groupName && rb.Checked == true)
ret = rb.Attributes["Value"];
}
}
return ret;
}
Here's a shorter version using Linq to avoid the loops...
public static string GetRadioValue(ControlCollection controls, string groupName)
{
var selectedRadioButton = controls.OfType<RadioButton>().FirstOrDefault(rb => rb.GroupName == groupName && rb.Checked);
return selectedRadioButton == null ? string.Empty : selectedRadioButton.Attributes["Value"];
}
Use LINQ:
container.Controls.OfType<RadioButton>().FirstOrDefault(r => r.GroupName == "GroupName" && r.Checked).Text;
The ToString() is not going to give you what you want: You need somthing more like
private string getRadioValue(ControlCollection clts, string groupName)
{
var ret = "";
foreach (Control ctl in clts)
{
if (ctl.Controls.Count != 0)
{
ret = getRadioValue(ctl.Controls, groupName);
}
if (!string.IsNullOrEmpty(ret)) {
return ret;
}
var rb = ctl as RadioButton;
if (rb != null && rb.GroupName == groupName && rb.Checked)
return = rb.Attributes["Value"];
}
}
return ret;
}
Even more shorter version (in my example I needed the Text of the Radio) Button.
You can also make it as an extension method of HtmlGenericControl which would be div for example.
public static class HtmlGenericControlExtensions
{
/// <summary>
/// Gets selected value of radio button by group name.
/// </summary>
/// <param name="controls">Html generic control.</param>
/// <param name="groupName">Name of the button group.</param>
/// <returns>Selected radio button name.</returns>
public static string GetSelectedRadioButtonName(this HtmlGenericControl control, string groupName)
=> control.Controls
.OfType<RadioButton>()
.FirstOrDefault(rb => rb.GroupName.Equals(groupName) && rb.Checked)?.Text ?? string.Empty;
}

Categories