My UI is build with lots of user controls that are translatable. Some controls in the usercontrols shouldn't be translated and I want to tag them as such with a [DoNotTranslate]` custom attribute.
in my userControl.designer.cs file
[DoNotTranslate]
private DevExpress.XtraEditors.LabelControl maxLabel;
[DoNotTranslate]
private DevExpress.XtraEditors.LabelControl valueLabel;
//all other controls
The translation function expects a (user)control and then goes through all the child control.Controls to make sure all controls are translated without the need to call the translation function on every single control.
Is it possible to find out if a control has my custom attribute set? The problem is that I don't see how I can get the attribute information in the translation function when i go through all the Controls.
Any advice is greatly appreciated,
Thanks
EDIT: I see now from the code you posted that the attribute is on a property of the control, not on the class defining the control itself. You can try like this:
public IEnumerable<PropertyInfo> GetNonTranslatableProperties(WebControl control)
{
foreach (PropertyInfo property in control.GetType().GetProperties())
{
if(
property
.GetCustomAttributes(true)
.Count(item => item is DoNotTranslateAttribute) > 0)
yield return property;
}
}
otherwise, you can subclass Label to a NonTranslatableLabel, apply the attribute to the class and use it instead of Label in your "father" control.
[NonTranslatable]
public class NonTranslatableLabel : Label
===================
for each of your controls you can do:
myCustomControl.GetType()
.GetCustomAttributes(true)
.Where(item => item is DoNotTranslateAttribute);
for instance you could enumerate all your "non-translatable" controls like this:
public IEnumerable<Control> GetNonTranslatableChildren(Control control)
{
foreach(Control c in control.Controls)
{
if(
c.GetType()
.GetCustomAttributes(true)
.Count(item => item is DoNotTranslateAttribute) > 0)
yield return c;
}
}
You can use GetCustomAttributes method to find out if the attribute has been applied or not. For example,
static readonly Type _DoNotTranslateAttribute = typeof(DoNotTranslate);
.... // other code
var t = control.GetType();
if (t.GetCustomAttributes(_DoNotTranslateAttribute, false).length > 0)
{
// do not translate
}
(disclaimer: untested/uncompiled code- just to give an idea how to use the function)
Related
This will be easiest if I first explain my code structure, then try to ask my question.
I have a base class containing some simple properties. We will call this BaseClass. I then have several other classes that extend BaseClass. Let's call these SubClass1, Subclass2, and SubClass3. Now I have this view model, and it contains this private member:
private ObservableCollection<BaseClass> objs = new ObservableCollection<BaseClass>();
The initializer of the view model contains something like this:
objs.Add(new SubClass1(attribute1, attribute2));
objs.Add(new SubClass3(attribute1, attribute2));
objs.Add(new SubClass1(attribute1, attribute2));
objs.Add(new SubClass2(attribute1, attribute2));
Now, in this case, I have 3 separate datagrids. I want all three to draw from objs, but I want one to show only objects of type SubClass1, one to show only objects of type SubClass2,and the last to show only objects of type SubClass3. I have successfully achieved this by creating a property for each as follows:
public ObservableCollection<SubClass1> SubClass1Objs
{
get
{
ObservableCollection<SubClass1> subObjs = new ObseleCollection<SubClass1>();
if (objs != null)
foreach (BaseClass obj in objs)
if(obj.GetType() == typeof(SubClass1))
subObjs .Add((SubClass1)obj);
return subObjs ;
}
}
The other 2 are identical. I bind the ItemSource of each datagrid to their property. This all works.
The user can currently edit any item in the data grid and the changes are reflected in objs. However, if the user tries to add an item to the datagrid, the items are not added to objs, and rightly so; I have no mutators(setters) for my properties. This is where my issues is. I am having trouble coming up with what the mutators would look like to add a new object to the collection. Does any one have any ideas? Thanks!
Thanks to #user49104 I was able to figure it out. I will post the answer here for anyone else who needs it.
I changed the properties of my SubClass1, SubClass2, and SubClass3 Collections to have normal more normal accessors and mutators and created a private member for each:
private ObservableCollection<SubClass1> _SubClass1Objs ;
public ObservableCollection<SubClass1> SubClass1Objs {
get { if (_SubClass1Objs== null) _SubClass1Objs = new ObservableCollection<SubClass1>(); return _SubClass1Objs; }
set { if (_SubClass1Objs!= value) { _SubClass1Objs= value; RaisePropertyChanged("SubClass1Objs"); } }
}
In the initializer of my view model, set the SubClass data :
SubClass1Objs= new ObservableCollection<SubClass1>();
if (objs != null)
foreach (BaseClass obj in objs.Where(c => c.GetType() == typeof(SubClass1)))
SubClass1Objs.Add((SubClass1)card);;
SubClass1Objs.CollectionChanged += SubClass1Objs_CollectionChanged;
Then lastly, in the CollectionChanged event, I check through to make sure no objects have been added or deleted and fix up objs:
// Check if a card was added
foreach (SubClass1 obj in SubClass1Objs)
if (!objs.Contains(obj))
objs.Add(obj);
// Check if a card has been deleted
for (int i = 0; i < objs.Where(c => c.GetType() == typeof(SubClass1)).Count(); ++i)
{
BaseClass obj = objs.Where(c => c.GetType() == typeof(SubClass1)).ElementAt(i);
if (!SubClass1Objs.Contains(obj))
objs.Remove(obj);
}
I want to get a reference to the TextBox bound to a given property name.
I would like to do this without changing the view.
Is there a proper way to do this using Caliburn Micro?
If not, what is a "good enough" way?
public class MweViewModel : PropertyChangedBase
{
public MweViewModel() : base()
{
PropertyChanged += (object sender, PropertyChangedEventArgs e) =>
{
// Find control (i.e. TextBox) bound to property with name e.PropertyName
TextBox textBox = ...
};
}
}
I'm not necessarily sure this is the most sensible approach (it's not something I've tried to do myself), looking at the Documentation, there's the mention of a ViewModelBinder class that's responsible for fixing up the various bindings of properties, methods, etc. to their respective ViewModels.
The BindProperties function on the ViewModelBinder is responsible for resolving the bindings between your properties, and the UI elements to which they're eventually bound. You could, define your own function based on the existing code, which kept track of all the bindings being established, so you would have a record of them you could use elsewhere in your program.
Using the existing code would give you something like this:
ViewModelBinder.BindProperties = (namedElements, viewModelType) =>
{
var unmatchedElements = new List<FrameworkElement>();
foreach (var element in namedElements)
{
var cleanName = element.Name.Trim('_');
var parts = cleanName.Split(new[] { '_' }, StringSplitOptions.RemoveEmptyEntries);
var property = viewModelType.GetPropertyCaseInsensitive(parts[0]);
var interpretedViewModelType = viewModelType;
for (int i = 1; i < parts.Length && property != null; i++)
{
interpretedViewModelType = property.PropertyType;
property = interpretedViewModelType.GetPropertyCaseInsensitive(parts[i]);
}
if (property == null)
{
unmatchedElements.Add(element);
// Log.Info("Binding Convention Not Applied: Element {0} did not match a property.", element.Name);
continue;
}
var convention = ConventionManager.GetElementConvention(element.GetType());
if (convention == null)
{
unmatchedElements.Add(element);
// Log.Warn("Binding Convention Not Applied: No conventions configured for {0}.", element.GetType());
continue;
}
var applied = convention.ApplyBinding(
interpretedViewModelType,
cleanName.Replace('_', '.'),
property,
element,
convention
);
if (applied)
{
// Log.Info("Binding Convention Applied: Element {0}.", element.Name);
}
else
{
// Log.Info("Binding Convention Not Applied: Element {0} has existing binding.", element.Name);
unmatchedElements.Add(element);
}
}
return unmatchedElements;
};
At the point when the binding is being added (when applied is set), you have all the information you require. You could then store specific bindings (e.g those relating to a TextBox).
You might use something like a static dictionary (there may be something far more appropriate depending on your requirement):
ViewModel Type Bound Property List of Bound elements
| | |
| | |
Dictionary<Type, Dictionary<PropertyInfo, List<FrameworkElement>>>
You would have to be careful about null/sanity checks.
There are a few other solutions that use helper methods to grab the bound properties/controls, although they often have to traverse the visual tree, this way, you're doing it at the point the binding is actually created.
I'm with a problem that I simply cannot solve, I've been researching for hours but with no results. Please help!
What I'm trying to do:
I have a User Control that list a class on my form and I want it to make changes on the main form if someone selects a different item on the list.
So I created an event:
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
if (listBox1.SelectedIndex != -1)
{
Object item = this.List.GetType().GetProperty("Item").GetValue(this.List, new Object[] { listBox1.SelectedIndex });
Control a = this.TopLevelControl;
Object temp = a.GetType().GetProperty("currentExpression").GetValue( a, null );
a.GetType().GetProperty("currentExpression").SetValue(temp, item, null);
}
}
In this code 'a' holds the main form. ( And this event runs in the User Control) So I have all that I need.
The List object is a List of Expressions. And the currentExpression property on the Main form is a Expression.
I want to put the Expression ( which I called item ) on the property currentExpression ( which is a Expression on the MainForm). But it allways says "Object does not match target type." TargetException was unhandled
When I debug I can see that both of them are correct. ( temp and item ) But it still throws an exception.
EDIT:
I must say that, the "List" object is not a List<Something>, it is the object to be listed, so List is an object ( object List ). My user control is generic, Thats why I'm using reflection.
This way I can have objectList1.ShowList(ListExpressions, "OriginalExpression");
where is this case ListExpressions is a List, but is sent to the user control as a object. Through reflection I can check which type of List it is and then read the property "OriginalExpression" and display a list.
So I'm gonna have a list of the property "OriginalExpression".
The User Control works fine, the problem is making this last part work. When I click on an item I get that message "Object does not match target type.".
Any ideas on how to do that?
Thanks a lot!
Why do you need to do all this with reflection? You don't say anything about that.
It looks like a complex way of writing:
var item = this.List[listBox1.SelectedIndex];
var a = this.TopLevelControl;
var temp = a.currentExpression;
temp.currentExpression = item; // ???
except that the last line (which i marked ???) looks strange since you GetType() on your a, find a property currentExpression from the type from a, and then use temp as the instance on which to set the property (set to item). Here's the line from your question I'm talking about:
a.GetType().GetProperty("currentExpression").SetValue(temp, item, null);
Why not implement it this way:
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
if ( listBox1.SelectedIndex != -1 )
{
var item = this.List.Item[listBox1.SelectedIndex];
var mainControl = this.TopLevelControl as IExpressionProvider;
if ( mainControl != null )
mainControl.CurrentExpression = item;
}
}
and in your main form just implement this simple interface:
public interface IExpressionProvider
{
YourExpressionType CurrentExpression { get; set; }
}
I solved the problem,
It's not exacly what I was looking for, but it is working!
I changed the
Control a = this.TopLevelControl;
to
mainForm a = (mainForm)this.TopLevelControl;
And the
a.GetType().GetProperty("currentExpression").SetValue(temp, (NCalc.Expression)item, null);
to
a.currentExpression = ( NCalc.Expression )item;
That solved my problem because I'm avoiding reflection. Thanks guys! –
I'm fairly new to C# and got a bit of a problem that I'm sure has a good solution using LINQ.
Background:
There's a project I've inherited that uses CrystalReports, the reports themselves all have viewer-forms [containing components/controls] associated with them in a uniform way (I'm fairly sure they were machine generated), one of these is a component descended from ReportClass which contains a database property. The database property is the only thing that differentiates a method (Log_On_Database) that appears in all of these classes. What I would like to do is create a common base class that searches the form for ReportClass and uses that to populate its local database variable w/ the value from the property so I can implement Log_On_Database in a single location.
Question:
How does one use LINQ to get all of the components (not [just] controls) belonging to a form and recursively for those that are controls (and thus can have their own)?
NOTE: Getting the result in a List would be great, as I could then test for a length of 0 (something went horribly wrong), 1 (expected), or more (and then I could do what I need to on those odd cases) -- even if this is all generated code, I don't trust it to not have been modified in horrendously painful ways.
So far I've got this:
// Get all controls of a certain type:
// http://stackoverflow.com/questions/3419159/how-to-get-all-child-controls-of-a-winform-of-a-specific-type-button-textbox
public IEnumerable<Control> GetAll(Control control, Type type)
{
var controls = control.Controls.Cast<Control>();
return controls.SelectMany(ctrl => GetAll(ctrl, type))
.Concat(controls)
.Where(c => c.GetType() == type);
}
protected ComponentCollection get_components(Component c)
{
Type parent = c.GetType();
FieldInfo fieldInfo = parent.GetField("components", BindingFlags.Instance | BindingFlags.NonPublic);
IContainer fieldData = (IContainer)fieldInfo.GetValue(components);
return fieldData.Components;
}
protected void Log_On_Database()
{
// ReportClass decends from ReportDocument, which has the Database property we're interested in
// this method grabs up any ReportDocument and decended objects. There should be only one.
List<ReportDocument> reports = new List<ReportDocument>();
// The list 'ctrls' contains all the Controls of 'this' form.
List<Control> ctrls = GetAll(this, typeof(Control)).ToList();
// Now we add all the components from all the controls which are ReportDocuments to the "reports" list.
foreach (Control c in ctrls)
foreach( Component x in get_components(c) )
{
if (x is ReportDocument)
reports.Add((ReportDocument)x);
}
switch (reports.Count)
{
case 0:
MessageBox.Show("No report document found.");
break;
case 1:
Log_On_Database( ((ReportDocument)reports[0]).Database );
break;
default:
MessageBox.Show("Too many report documents found.");
break;
} // end switch
} // end Log_On_Database
It would be nice to get it all in one LINQ statement.
With linq you can't do recursive queries, so the GetAll should remain as it is.
But then you can do:
var reports = GetAll(this, typeof(Control))
.SelectMany(c => get_components(c))
.OfType<ReportDocument>()
.ToList();
How can I get all the controls in a namespace? For example, I want to get the controls in System.Windows.Forms: TextBox, ComboBox etc.
The notion of a control in a namespace is a bit unclear. You could use reflection to get classes in an assembly in a given namespace which derive from a particular base type. For example:
class Program
{
static void Main()
{
var controlType = typeof(Control);
var controls = controlType
.Assembly
.GetTypes()
.Where(t => controlType.IsAssignableFrom(t) &&
t.Namespace == "System.Windows.Forms"
);
foreach (var control in controls)
{
Console.WriteLine(control);
}
}
}
this will return all classes in a specified namespace :
string #namespace = "System.Windows.Forms";
var items = (from t in Assembly.Load("System.Windows.Forms").GetTypes()
where t.IsClass && t.Namespace == #namespace
&& t.IsAssignableFrom(typeof(Control))
select t).ToList();
Your form object has a Controls member, which is of type ControlCollection. It is essentially a list (with some other interfaces under the hood) of all of the controls.
EDIT: As per your comment, you need to cast the control back into a textbox. First you must identify it as a control.
foreach (var control in controls)
{
if(control is TextBox)
{
(control as TextBox).Text = "Or whatever you need to do";
}
}