How to set a property on a set of controls - c#

So, I am doing this, wanting to be cool:
List<CheckButton> cbuttons = new List<CheckButton>(
new CheckButton[] {
cbShowAll,
cbShowApproved,
cbShowNew,
cbShowQC,
cbShowSent });
cbuttons.ForEach(b => b.LookAndFeel.UseDefaultLookAndFeel = false);
Is there a more cool way to do it?
EDIT:
There might be CheckButtons that won't be affected. There might be multiple property and/or method calls per group.

To make it applicable for many such controls like TextBox or CheckBox, create an extension method on the Form like so:
public static class FormExtensions
{
public static void ChangeAll<T>(this Form form, string propName, object value) where T : Control
{
foreach (Control c in form.Controls.OfType<T>())
{
PropertyInfo myPropInfo = typeof(T).GetProperty(propName);
if (myPropInfo != null)
{
myPropInfo.SetValue(c, value, null);
}
}
}
}
and we can call it:
this.ChangeAll<CheckBox>("Checked", false);
this.ChangeAll<Button>("Text", "DefaultButtonName");
this.ChangeAll<TextBox>("Text", "DefaultText");
where this is Form1 : Form

Unless that list is dynamic, doing it the “boring” way will be likely more efficient:
cbShowAll.LookAndFeel.UseDefaultLookAndFeel = false;
cbShowApproved.LookAndFeel.UseDefaultLookAndFeel = false;
cbShowNew.LookAndFeel.UseDefaultLookAndFeel = false;
cbShowQC.LookAndFeel.UseDefaultLookAndFeel = false;
cbShowSent.LookAndFeel.UseDefaultLookAndFeel = false;
Other than that, you could subclass the CheckButton and make that the default value too.
Also, in general “readable and possibly efficient” is much better than “cool and fancy”.

If you're performing some one-off initialization on a particular subset of controls (rather than all of them), then you could create an array containing that subset and then enumerate the array:
foreach (CheckButton button in
new[] { cbShowAll, cbShowApproved, cbShowNew, cbShowQC, cbShowSent })
{
button.LookAndFeel.UseDefaultLookAndFeel = false;
}
Using a loop allows you to avoid duplicate code (as in poke's answer).
Note: Eric Lippert wrote a blog post explaining why he prefers foreach over ForEach. Excerpt:
[Providing a ForEach extension method] lets you rewrite this perfectly clear code:
foreach (Foo foo in foos) { statement involving foo; }
into this code:
foos.ForEach((Foo foo) => { statement involving foo; });
which uses almost exactly the same characters in slightly different order. And yet the second version is harder to understand, harder to debug, and introduces closure semantics, thereby potentially changing object lifetimes in subtle ways.
Also, not all versions of the .NET Framework support List<T>.ForEach. For example, if you're writing a Windows Store app, you need to use foreach instead.

If you want to do that with all the CheckBoxes you have inside of a form or control, you don't need to hard code the array. If you're hard coding everything, then I agree with poke's answer.
However, you could be totally dynamic and if this is something you want to be reusing in many places, you could have a utility method (or something in a base form or control class that fires after the InitializeComponents() method).
If you want that, you could do something like this:
this.Controls.OfType<CheckBox>()
.ForEach(b => b.LookAndFeel.UseDefaultLookAndFeel = false);
If you don't want to do that with every CheckBox, you could create a subclass of those, and substitute that class name in the OfType generic declaration.

Related

GTK equivalent to Control.Controls from System.Windows.Forms

I'm trying to find the GTK equivalent of Control.Controls, which is something that is only available in System.Windows.Forms, same thing for ControlCollection.
So I was wondering if anyone knew the GTK equivalent to that, or if there's a solution to making something similar in GTK? Because those functions had features like add and remove, but I'm unable to find anything similar in that regard.
The code that I have with me right now that I wish to change, is this:
private void CreatePianoKeys()
{
// If piano keys have already been created.
if (keys != null)
{
// Remove and dispose of current piano keys.
foreach (PianoKey key in keys)
{
Controls.Remove(key);
key.Dispose();
}
}
keys = new PianoKey[HighNoteID - LowNoteID];
whiteKeyCount = 0;
for (int i = 0; i < keys.Length; i++)
{
keys[i] = new PianoKey(this);
keys[i].NoteID = i + LowNoteID;
if (KeyTypeTable[keys[i].NoteID] == KeyType.White)
{
whiteKeyCount++;
}
else
{
keys[i].NoteOffColor = Color.Black;
keys[i].BringToFront();
}
keys[i].NoteOnColor = NoteOnColor;
Controls.Add(keys[i]);
}
}
Because I'm currently trying to change the UI from WinForms to GTK, so it can work on Linux distributions.
Any help with this would be greatly appreciated! Thank you!
Yes, but just like in .NET, in GTK, only certain kinds of widgets ("Controls" in .NET-speak) can specify its children (like the ControlCollection in your example). These parent widgets are usually objects that derive from the Gtk.Container base class, which implements the Gtk.Buildable interface.
You can add children to any Container object by using its Add() or Remove() methods, just like you do with the ControlCollection object In C#. Since this is a piano, you could use a Gtk.Box (or a subclass of it), which is a container that shows its children widgets in a single row back-to-back if specified as horizontally-oriented. Since I don't know what sort of class your PianoKey is, I can't suggest a good alternative in GTK, but hopefully that helps.

XamlBindingHelper Class

Can somebody provide an overview for use of the XamlBindingHelper class with examples? Specifically the GetDataTemplateComponent and SetDataTemplateComponent method.
In the official document, it says
This class is for use in code that is generated by the XAML compiler.
This tells me that I should be able to find some reference of it in code-generated classes (.g.cs) by x:Bind, given there's not a single thread on the Internet that explains what exactly it does.
So I created a test UWP project with a ListView, and inside its ItemTemplate I threw in some x:Bind with x:Phase. After I compiled the project, I found some of its methods used inside my MainPage.g.cs -
XamlBindingHelper.ConvertValue
public static void Set_Windows_UI_Xaml_Controls_ItemsControl_ItemsSource(global::Windows.UI.Xaml.Controls.ItemsControl obj, global::System.Object value, string targetNullValue)
{
if (value == null && targetNullValue != null)
{
value = (global::System.Object) global::Windows.UI.Xaml.Markup.XamlBindingHelper.ConvertValue(typeof(global::System.Object), targetNullValue);
}
obj.ItemsSource = value;
}
Apparently the XamlBindingHelper.ConvertValue method is for converting values. I knew this already, as I used it in one of my recent answers on SO.
XamlBindingHelper.SuspendRendering & XamlBindingHelper.ResumeRendering
public int ProcessBindings(global::Windows.UI.Xaml.Controls.ContainerContentChangingEventArgs args)
{
int nextPhase = -1;
switch(args.Phase)
{
case 0:
nextPhase = 1;
this.SetDataRoot(args.Item);
if (!removedDataContextHandler)
{
removedDataContextHandler = true;
((global::Windows.UI.Xaml.Controls.StackPanel)args.ItemContainer.ContentTemplateRoot).DataContextChanged -= this.DataContextChangedHandler;
}
this.initialized = true;
break;
case 1:
global::Windows.UI.Xaml.Markup.XamlBindingHelper.ResumeRendering(this.obj4);
nextPhase = -1;
break;
}
this.Update_((global::System.String) args.Item, 1 << (int)args.Phase);
return nextPhase;
}
public void ResetTemplate()
{
this.bindingsTracking.ReleaseAllListeners();
global::Windows.UI.Xaml.Markup.XamlBindingHelper.SuspendRendering(this.obj4);
}
XamlBindingHelper.SuspendRendering & XamlBindingHelper.ResumeRendering look very interesting. They seem to be the key functions to enable ListView/GridView's incremental item rendering which helps improve the overall panning/scrolling experience.
So apart from x:DeferLoadingStrategy and x:Load(Creators Update), they are something else that could be used to improve your app performance.
IDataTemplateComponent & IDataTemplateExtension
However, I couldn't find anything related to GetDataTemplateComponent and SetDataTemplateComponent. I even tried to manually set this attached property in XAML but the get method always returned null.
And here's the interesting bit. I later found this piece of code in the generated class.
case 2: // MainPage.xaml line 13
{
global::Windows.UI.Xaml.Controls.Grid element2 = (global::Windows.UI.Xaml.Controls.Grid)target;
MainPage_obj2_Bindings bindings = new MainPage_obj2_Bindings();
returnValue = bindings;
bindings.SetDataRoot(element2.DataContext);
element2.DataContextChanged += bindings.DataContextChangedHandler;
global::Windows.UI.Xaml.DataTemplate.SetExtensionInstance(element2, bindings);
}
break;
The method DataTemplate.SetExtensionInstance looks very similar to XamlBindingHelper.SetDataTemplateComponent. It takes element2 which is the root Grid inside the ItemTemplate of my ListView, and an IDataTemplateExtension; where the latter takes an element and an IDataTemplateComponent. If you have a look at their definitions, their functionalities are very similar, which makes me think if DataTemplate.SetExtensionInstance is the replacement of XamlBindingHelper.SetDataTemplateComponent? I'd love to know if otherwise.
Unlike IDataTemplateComponent, you can get an instance of the IDataTemplateExtension in your code -
var firstItemContainer = (ListViewItem)MyListView.ContainerFromIndex(0);
var rootGrid = (Grid)firstItemContainer?.ContentTemplateRoot;
var dataTemplateEx = DataTemplate.GetExtensionInstance(rootGrid);
In my case, the dataTemplateEx is an instance of another generated class called MainPage_obj2_Bindings, where you have access to methods like ResetTemplate and ProcessBindings.
I assume they could be helpful if you were to build your own custom list controls, but other than that I just can't see why you would ever need them.

Copying a TabItem with an MVVM structure

This is an attempt to expand on this question. In my WPF program I've been cloning tabItems by using an XamlWriter in a function called TrycloneElement. I originally found this function here, but the function can also be viewed in the link to my previous question.
Now that I am beginning to worry about functionality inside my program, I found that the TrycloneElement function does not replicate any code-behind functionality assigned to the tabItem that it is cloning.
Because of High Core's link and comment on my earlier question I decided to start implementing functionality on my tabItems through Data Binding with my ViewModel.
Here is a sample of a command that I've implemented:
public viewModel()
{
allowReversing = new Command(allowReversing_Operations);
}
public Command AllowReversing
{
get { return allowReversing; }
}
private Command allowReversing;
private void allowReversing_Operations()
{
//Query for Window1
var mainWindow = Application.Current.Windows
.Cast<Window1>()
.FirstOrDefault(window => window is Window1) as Window1;
if (mainWindow.checkBox1.IsChecked == true) //Checked
{
mainWindow.checkBox9.IsEnabled = true;
mainWindow.groupBox7.IsEnabled = true;
}
else //UnChecked
{
mainWindow.checkBox9.IsEnabled = false;
mainWindow.checkBox9.IsChecked = false;
mainWindow.groupBox7.IsEnabled = false;
}
}
*NOTE: I know that I cheated and interacted directly with my View in the above code, but I wasn't sure how else to run those commands. If it is a problem, or there is another way, please show me how I can run those same commands without interacting with the View like I did.
Now to the question:
After changing my code and adding the commands to my ViewModel, the TrycloneElement function no longer works. At run time during the tab clone I receive an XamlParseException on line, object x = XamlReader.Load(xmlReader); that reads:
I'm fine with ditching the function if there is a better way and I don't need it anymore. But ultimately, how do I take a tabItem's design and functionality and clone it? (Please keep in mind that I really am trying to correct my structure)
Thank you for your help.
Revision of Leo's answer
This is the current version of Leo's answer that I have compiling. (There were some syntax errors)
public static IList<DependencyProperty> GetAllProperties(DependencyObject obj)
{
return (from PropertyDescriptor pd in TypeDescriptor.GetProperties(obj, new Attribute[] { new PropertyFilterAttribute(PropertyFilterOptions.SetValues) })
select DependencyPropertyDescriptor.FromProperty(pd)
into dpd
where dpd != null
select dpd.DependencyProperty).ToList();
}
public static void CopyPropertiesFrom(this FrameworkElement controlToSet,
FrameworkElement controlToCopy)
{
foreach (var dependencyValue in GetAllProperties(controlToCopy)
.Where((item) => !item.ReadOnly)
.ToDictionary(dependencyProperty => dependencyProperty, controlToCopy.GetValue))
{
controlToSet.SetValue(dependencyValue.Key, dependencyValue.Value);
}
}
Here is my example of a properly-implemented dynamic TabControl in WPF.
The main idea is that each Tab Item is a separate widget that contains its own logic and data, which is handled by the ViewModel, while the UI does what the UI must do: show data, not contain data.
The bottom line is that all data and functionality is managed at the ViewModel / Model levels, and since the TabControl is bound to an ObservableCollection, you simply add another element to that Collection whenever you need to add a new Tab.
This removes the need for "cloning" the UI or do any other weird manipulations with it.
1.) To fix that XamlParseException, make sure you have a public constructor like an empty one, you probably defined a constructor and when you tried to serialize that object and deserialize it can't. You have to explicitly add the default constructor.
2.) I don't like the word clone, but I'd say, when they want to copy. I'll manually create a new tab item control then do reflection on it.
I have this code that I made
public static IList<DependencyProperty> GetAllProperties(DependencyObject obj)
{
return (from PropertyDescriptor pd in TypeDescriptor.GetProperties(obj, new Attribute[] {new PropertyFilterAttribute(PropertyFilterOptions.SetValues)})
select DependencyPropertyDescriptor.FromProperty(pd)
into dpd where dpd != null select dpd.DependencyProperty).ToList();
}
public static void CopyPropertiesFrom(this FrameworkElement controlToSet,
FrameworkElement controlToCopy)
{
foreach (var dependencyValue in GetAllProperties(controlToCopy)
.Where((item) => !item.ReadOnly))
.ToDictionary(dependencyProperty => dependencyProperty, controlToCopy.GetValue))
{
controlToSet.SetValue(dependencyValue.Key, dependencyValue.Value);
}
}
So it would be like
var newTabItem = new TabItem();
newTabItem.CopyPropertiesFrom(masterTab);

Checking Multiple textbox if they're null or whitespace

I have a form where I have lots of textboxes and all of them are required to be filled out. In C# how do I actually if check there are group of fields having a null or whitespace?
I am familiar with string.isNullOrWhiteSpace(string here) but I don't want to do multiple if statements of that, it would result in a bad code.
I am trying to avoid something like this
if(string.isNullOrWhiteSpace(string here)
|| string.isNullOrWhiteSpace(string here)
|| string.isNullOrWhiteSpace(string here))
{
// do something
}
Are there fix for this type of bad code?
You can query the controls collection of the form (or relevant container) and filter for textboxes and further query to see if any are empty (none should really have null values). Example:
var emptyTextboxes = from tb in this.Controls.OfType<TextBox>()
where string.IsNullOrEmpty(tb.Text)
select tb;
if (emptyTextboxes.Any())
{
// one or more textboxes are empty
}
You can do effectively the same thing using the fluent syntax.
bool isIncomplete = this.Controls.OfType<TextBox>().Any(tb => string.IsNullOrEmpty(tb.Text));
if (isIncomplete)
{
// do your work
}
For this code, you should be working with at least Visual Studio 2008 / C# 3 / .NET 3.5. Your project needs to have a reference to System.Core.dll (should have one by default) and you need a using System.Linq; directive in the class file.
Based upon your comments, consider another method if you are having trouble understanding or working with the linq version. You can certainly do this in an explicit loop (the Linq code will ultimately be a loop as well). Consider
bool isIncomplete = false;
foreach (Control control in this.Controls)
{
if (control is TextBox)
{
TextBox tb = control as TextBox;
if (string.IsNullOrEmpty(tb.Text))
{
isIncomplete = true;
break;
}
}
}
if (isIncomplete)
{
}
Finally, this code is written as if all of the textboxes are in a single container. That container might be the form, a panel, etc. You will need to point to the appropriate container (eg., instead of this (the form) it might be this.SomePanel). If you are working with controls that are in multiple and perhaps nested containers, you will need to do more work to find them programmatically (recursive searching, explicit concatenation, etc.) or you might just preload the references into an array or other collection. For example
var textboxes = new [] { textbox1, textbox2, textbox3, /* etc */ };
// write query against textboxes instead of this.Controls
You said you have multiple GroupBox controls. If each GroupBox is loaded onto the form and not nested in another control, this may get you started.
var emptyTextboxes = from groupBox in this.Controls.OfType<GroupBox>()
from tb in groupBox.Controls.OfType<TextBox>()
where string.IsNullOrEmpty(tb.Text)
select tb;
That depends on what you consider "bad code." Depending on your requirements what text boxes are required to be filled out can vary. Further, even if all of the fields are required all of the time you still want to give friendly error messages letting people know which field they didn't fill out. There a variety of approaches to solving this issue depending on how you are rendering your form. Since you haven't specified any here's a very direct method for doing so.
var incompleteTextBoxes = this.Controls.OfType<TextBox>()
.Where(tb => string.IsNullOrWhiteSpace(tb.Text));
foreach (var textBox in inCompleteTextBoxes)
{
// give user feedback about which text boxes they have yet to fill out
}
Yet another solution.
This will recursively travel the whole control Tree , and Check for null or empty text in all of the textboxes.
caveat -
If you have some fancy controls not inheriting from the standard Winforms textbox - check will not be performed
bool check(Control root,List<Control> nonFilled)
{
bool result =true;
if (root is TextBox && string.isNullOrEmpty(((TextBox)root).Text) )
{
nonFilled.Add(root);
return false;
}
foreach(Control c in root.Controls)
{
result|=check(c,nonFilled)
}
return result;
}
Usage :
List<Control> emptytextboxes=new List<Control>()
bool isOK=check(form, emptytextboxes);

How to find a control or page a control is embedded In

I've written a web user control which I want to be able to drop into the markup for either aspx pages or other web user controls.
I need my user control to be able to easily and efficiently work out if its inside another user control or an aspx page. My initial idea is to do it recursively with checks on the Parent property - continue looking up the nesting hierarchy until I find either a web form or a user control - but I'm not sure this the best way of going about this.
Can you suggest an easier way? Thanks.
Recursively check the type of your Parent until Parent.GetType() is either typeof(UserControl) or type(Page)
private bool IsAncestorTypeOf(Control c, params Type[] typesToCheck)
{
var parent = c.Parent;
if (parent == null) return false;
if (typesToCheck.Contains(parent.GetType())) return true;
return IsAncestorTypeOf(parent, typesToCheck);
}
Or the same without recursion
private bool IsAncestorTypeOf(Control c, params Type[] typesToCheck)
{
var parent = c.Parent;
while (true)
{
if (parent == null) return false;
if (typesToCheck.Contains(parent.GetType())) return true;
parent = parent.Parent;
}
}
Call it like
var isAncestorPageOrUserControl = IsAncestorTypeOf(this, typeof(Page), typeof(UserControl));
or
var isAncestorPage = IsAncestorTypeOf(this, typeof(Page));
var isAncestorUserControl = IsAncestorTypeOf(this, typeof(UserControl));
Generally, components should be unaware of their arbitrary containers, although the containers must know their components (unless it's a strong dependency situation like list items are always in a list type and you can make a strong two way relationship). However it sounds like you are reaching out into the general surroundings. You might find many cases to code for doing this and accidentally miss others.
By making the user control aware of its surroundings and the larger world you may be introducing dependencies that make your control less reusable and harder to maintain.
If something the control needs is outside of itself, you might move toward composition by forcing the developer to provide a reference to the needed thing on a property of your user control. This is the way, for example, that validation controls in ASP.NET do it, to reference an external control to validate by id.
Of course what I specified is practical only some of the time. Is there a specific reason or edge case why you need to make your user control look around itself, or can you get away with providing instructions to the developer about where the control should be used?
This should work:
C#
bool inPage = (this.NamingContainer == this.Page);
VB.NET
Dim inPage as Boolean = Me.NamingContainer is Me.Page
Edit: it seems to be not as simple as i hoped. If the usercontrol resists in a control like a GridViewRow, the NamingControl of it would be the Row and not the Page.
This takes it into account:
C#
public static bool isControlInPageOruserControl(Control uc)
{
bool inPage = uc.NamingContainer is Page;
if (inPage) {
return true;
} else if (uc.NamingContainer is UserControl) {
return false;
} else {
return isControlInPageOruserControl(uc.NamingContainer);
}
}
VB.NET:
Public Shared Function isControlInPageOruserControl(ByVal uc As Control) As Boolean
Dim inPage As Boolean = TypeOf uc.NamingContainer Is Page
If inPage Then
Return True
ElseIf TypeOf uc.NamingContainer Is UserControl Then
Return False
Else
Return isControlInPageOruserControl(uc.NamingContainer)
End If
End Function

Categories