So, I have Form called Preferences with TabControl in it. This TabControl contains several TabPages(General, Advanced, Misc, ...) with few comboboxes, checkboxes and labels. Each of this control inside TabPage is assigned Application Settings Property Binding (aka they show saved user settings, user can change them etc...).
I know that there is a method to reset all settings (Properties.Settings.Default.Reset();), but is there a way how to reset only settings inside one TabPage?
My solution is to iterate thru controls in TabPage, check if it is combobox, label etc and then reset it´s value to default, but is there a "oneliner" solution to this ?
Use the following solution to get the original value of a single setting:
(The example assumes you want to get the ORIGINAL value of a setting named 'Username')
var defaultUsername = Properties.Settings.Default.GetType()
.GetProperty(nameof(Properties.Settings.Default.Username))
.GetCustomAttribute<System.Configuration.DefaultSettingValueAttribute>()
.Value;
Important - this solution will always return a string value. make sure to parse it properly, or use this extension method I wrote:
public static T GetDefaultValue<T>(this ApplicationSettingsBase settings, string settingKey)
{
return (T)Convert.ChangeType(settings.GetType()
.GetProperty(settingsKey)
.GetCustomAttribute<DefaultSettingValueAttribute>()
.Value, typeof(T));
}
Usage:
var defaultNumber = Properties.Settings.Default.GetDefaultValue<int>(nameof(Properties.Settings.Default.Number));
The ApplicationSettings doesn't have built-in support to reset just some properties. But to solve the problem, you can use either of these options:
Create a method which resets all bound controls of a TabPage
Using Multiple Settings Files with Designer Support
Option 1 - Create a method which resets all bound controls of a TabPage
You can create a method which look at controls of the tab page and check if it's bound to application settings, find the property in settings and reset its value to the default value. Then you can reset settings of a TebPage width one line of code: ResetSettings(tabPage1);.
Here is the method:
private void ResetSettings(TabPage tabPage)
{
foreach (Control c in tabPage.Controls)
{
foreach (Binding b in c.DataBindings)
{
if (b.DataSource is ApplicationSettingsBase)
{
var settings = (ApplicationSettingsBase)b.DataSource;
var key = b.BindingMemberInfo.BindingField;
var property = settings.Properties[key];
var defaultValue = property.DefaultValue;
var type = property.PropertyType;
var value = TypeDescriptor.GetConverter(type).ConvertFrom(defaultValue);
settings[key] = value;
//You can also save settings
settings.Save();
}
}
}
}
Option 2 - Using Multiple Settings Files with Designer Support
If the reason of using a single settings file is because of designer support, you should know you can have designer support also with multiple settings files. Then you can use different settings files and reset each settings group separately. You can simply encapsulate them in a single class using such code:
public static class MySettings
{
public static Sample.General General
{
get { return Sample.General.Default; }
}
public static Sample.Advanced Advanced
{
get { return Sample.Advanced.Default; }
}
public static void ResetAll()
{
General.Reset();
Advanced.Reset();
}
public static void SaveAll()
{
General.Save();
Advanced.Save();
}
}
To reset a setting group it's enough to call MySettings.General.Reset();
To reset all settings, you can call MySettings.ResetAll();
Note for design-time support
To have designer support for binding properties to settings, create multiple settings files in root of your project. Don't put settings files in folders. The setting picker, only shows Settings.settings file which is in Properties folder and those files which are in root of project. This way you can see different settings files and settings properties in a tree-view like this:
TabPage page = aTabControl.SelectedTab;
var controls = page.Controls;
foreach (var control in controls)
{
//do stuff
}
Try this:
private void button2_Click(object sender, EventArgs e)
{
TabPage page = tabControl1.SelectedTab;
var controls = page.Controls;
foreach (var control in controls)
{
if(control is TextBox)
{
//do stuff
}
if(control is ComboBox )
{
ComboBox comboBox = (ComboBox)control;
if (comboBox.Items.Count > 0)
comboBox.SelectedIndex = 0;
comboBox.Text = string.Empty;
}
}
}
Related
I want to know if there exists a way to set a control's Text property from a resource file in design time:
Or this process can only be performed programatically?
The designer only serializes string for Text property. You can not set the Text property to a resource value directly using designer.
Even if you open the Form1.Designer.cs file and add a line to initialization to set the Text property to a resource value like Resource1.Key1, after first change in designer, the designer replace your code by setting the string value of that resource for Text property.
In general I recommend using standard localization mechanisms of windows forms, using Localizable and Language property of Form.
But if in some reason you want to use your resource file and want to use a designer-based solution, as good option you can create an extender component to set the resource key for your control at design-time and then use it at run-time.
Code for the extender component is at the end of the post.
Usage
Make sure you have a resource file. For example Resources.resx in the properties folder. Also make sure you have some resource key/value in the resource file. For example Key1 with value = "Value1", Key2 with value = "Value2". Then:
Put a ControlTextExtender component on your form.
Using property grid set the ResourceClassName property of it to the full name of your resource file for example WindowsApplication1.Properties.Resources`
Select each control you want to set its Text and using property grid set the value of ResourceKey on controlTextExtender1 property to the resource key that you want.
Then run the application and see the result.
Result
and here is an screenshot of result, and as you see, I even localized Text property of the form this way.
Switch between Cultures at Run-Time
You can switch between cultures at run-time, without need to close and reopen the form simply using:
System.Threading.Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("fa");
this.controlTextExtender1.EndInit();
Implementation
Here is a basic implementation of the idea:
[ProvideProperty("ResourceKey", typeof(Control))]
public class ControlTextExtender
: Component, System.ComponentModel.IExtenderProvider, ISupportInitialize
{
private Hashtable Controls;
public ControlTextExtender() : base() { Controls = new Hashtable(); }
[Description("Full name of resource class, like YourAppNamespace.Resource1")]
public string ResourceClassName { get; set; }
public bool CanExtend(object extendee)
{
if (extendee is Control)
return true;
return false;
}
public string GetResourceKey(Control control)
{
return Controls[control] as string;
}
public void SetResourceKey(Control control, string key)
{
if (string.IsNullOrEmpty(key))
Controls.Remove(control);
else
Controls[control] = key;
}
public void BeginInit() { }
public void EndInit()
{
if (DesignMode)
return;
var resourceManage = new ResourceManager(this.ResourceClassName,
this.GetType().Assembly);
foreach (Control control in Controls.Keys)
{
string value = resourceManage.GetString(Controls[control] as string);
control.Text = value;
}
}
}
I have created two settings file under properties in c#. Basically I have to apply settings based on a particular set of condition
for example the settings file are "myset1" and "myset2"
both these settings have similar structure
myset1
price = 100
qty = 100
myset2
price = 150
qty = 20
in my application, if value of variable "appColor" is "blue" myset1 has to be used if variable "appColor" is "red" myset2 has to be used.
in my code
productPrice.Text = //based on the "appColor" selection value from myset1 or myset2 has to be displayed.
I tried this but not working
Settings setSelector = new Settings();
if(appColor == "blue")
{
setSelector = myset1.Default;
}
else(appColor == "red")
{
setSelector = myset2.Default;
}
I am getting error that "cannot convert source type "myset1" to settings"
EDIT: my aim is that productPrice.Text = setSelector.Price; remains same even when settings are changed so I don't have to change code here and just have to change the settings. basically full forms gets filled based on the settings selected.
any help would be appreciated
I tried below code. it worked correctly.
I have two Textboxes, in which i am setting Price and Qty based on color.
Object obj = new Object();
if(appColor == "blue")
{
obj = (System.Configuration.SettingsPropertyCollection)Properties.Settings.Default.Properties;
}
else(appColor == "red")
{
obj = (System.Configuration.SettingsPropertyCollection)Properties.Settings1.Default.Properties;
}
foreach (System.Configuration.SettingsProperty p in Properties.Settings.Default.Properties)
{
if (p.Name=="Qty")
textBox1.Text = p.DefaultValue.ToString();
else if (p.Name=="Price")
textBox2.Text = p.DefaultValue.ToString();
}
I Hope this helps :)
After playing around with it a bit and referencing this MSDN article specifically the last section on adding alternate sets of settings.
To Add an Additional Set of Settings
From the Project menu, choose Add New Item. The Add New Item dialog box opens.
In the Add New Item dialog box, select Settings File.
In the Name box, give the settings file a name, such as SpecialSettings.settings, and click Add to add the file to your
solution.
In Solution Explorer, drag the new settings file into the Properties folder. This allows your new settings to be available in
code.
Add and use settings in this file as you would any other settings file. You can access this group of settings through the
Properties.SpecialSettings object.
I then realized that each settings file is its own separate class therefore you have to go back to a common class. By doing so you will loose your individual properties and have to cast it to the proper class. I then looked at this SO question searching for dynamic casting. According to JaredPar's answer it appears that the easiest way to do this would be to use the dynamic keyword and let the class type be figured out at runtime.
i.e.
Class level Declaration:
dynamic setSelector;
Intializing it during Form Load:
private void Form1_Load(object sender, EventArgs e)
{
if(appColor == "blue")
{
setSelector = Properties.myset1.Default;
}
else if(appColor == "red")
{
setSelector = Properties.myset2.Default;
}
textBox1.Text = setSelector.qty.ToString();
}
I have a TabControl in which I want to prevent adding existing TabPage (they are identified by a name) and instead set the SelectedTabPage to this precise tab.
I wish to know if there are an event that triggers right before a page is being added to the TabControl. If not, would using the event CollectionChanged of the TabPages (list) be a correct alternative ?
I believe the event you're looking for is the Control.ControlAdded event:
http://msdn.microsoft.com/en-us/library/system.windows.forms.control.controladded.aspx
If that also detects when things inside the tab pages themselves are added, you should be able to filter out everything but TabPage controls using the ControlEventArgs.Control property in your event handler.
To reject adding a control will be a little more complicated. Since this event seems to only be raised after the control gets added, you'll need to do something like this:
void onControlAdded(object sender, ControlEventArgs e) {
var tab = e as TabPage;
if (tab == null)
return;
this.myTabControlObject.TabPages.Remove(tab);
}
This should remove the tab, but it will likely slow the tab adding process considerably.
Try something like this, I am checking the TabControl page Collection for a page with the same name as the Page that is trying to be added, if it exists I am setting focus to the existing instance, otherwise adding the new page to the TabControl. See if something like this works for you.
private void button1_Click(object sender, EventArgs e)
{
TabPage tp = new TabPage();
tp.Name = tabPage1.Name;
var temp =tabControl1.Controls.Find(tp.Name,true);
if( temp.Length > 0)
{
tabControl1.SelectedTab = (TabPage) temp[0];
}
else
tabControl1.Controls.Add(tp);
}
Anything having to do with the ControlCollection will most likely be triggered after the control has been added.
From above link:
You can determine if a Control is a member of the collection by passing the control into the Contains method. To get the index value of the location of a Control in the collection, pass the control into the IndexOf method. The collection can be copied into an array by calling the CopyTo method.
If you want you could cleanup your code some by adding an ExtensionMethod to your TabControl Check for an existing page, set focus or add from there.
Example:
namespace ExtensionMethods
{
public static class MyExtensions
{
public static bool AddPage(this TabControl tc, TabPage tp)
{
var matchedPages = tc.Controls.Find(tp.Name, false);
if ( matchedPages.Length > 0)
{
tc.SelectedTab = (TabPage)matchedPages[0];
return true;
}
else
{
tc.TabPages.Add(tp);
tc.SelectedTab = tp;
return false;
}
}
}
}
Usage:
tabControl1.AddPage(tp);
I am designing a comparison dialog (shows several widgets with their characteristics in a grid). There is a features section where all available features are listed with a check box for each one. If the part has that feature, the checkbox is checked. These checkboxes need to be read-only so I've isEnabled=false. However visually the checkboxes (and the label content) show as greyed out.
Here are some important points:
The checkbox is a visual indicator of whether a part has a feature. There is no requirement for interaction.
The requirement is for a checkbox; I'd have to convince the powers that be to use something different.
What I want is an easy way to style/controltemplate a checkbox (and it's content) so it looks enabled, but doesn't react to user input.
Microsoft provides some of their default styles on MSDN and you can find the default style for a checkbox here: http://msdn.microsoft.com/en-us/library/ms752319(v=vs.85).aspx. Copy this style into your project, remove the Trigger for IsEnabled and set the style for your checkboxes to this new style.
On a side note, I'd recommend copying the style into a separate ResourceDictionary for reusablitiy and to keep the style from cluttering up your xaml files.
Create a custom control by inheriting from CheckBox, and in its constructor create a Click handler for it. Within that Click handler, put the following code:
((CheckBox)sender).Checked = !((CheckBox)sender).Checked;
Here's a complete example.
For Windows Forms:
namespace System.Windows.Forms
{
public class UnChangingCheckBox : System.Windows.Forms.CheckBox
{
public UnChangingCheckBox()
{
this.Click += new EventHandler(UnChangingCheckBox_Click);
}
void UnChangingCheckBox_Click(object sender, EventArgs e)
{
((CheckBox)sender).Checked = !((CheckBox)sender).Checked;
}
}
}
For WPF:
namespace System.Windows.Controls
{
public class UnchangingCheckBox : System.Windows.Controls.CheckBox
{
public UnchangingCheckBox()
{
this.Click += new System.Windows.RoutedEventHandler(UnchangingCheckBox_Click);
}
void UnchangingCheckBox_Click(object sender, System.Windows.RoutedEventArgs e)
{
if (((CheckBox)sender).IsChecked.HasValue)
((CheckBox)sender).IsChecked = !((CheckBox)sender).IsChecked;
}
}
}
If you place the above code in a new class in your Windows Forms or WPF project, they'll appear as new tools in your toolbox. Then all you need to do is drag your new "UnchangingCheckBox" control onto your form where you were using a CheckBox. You don't need to do any coding on your form.
Using this approach your code will still be able to do everything you could do to a CheckBox (set its value, etc). It's only user interaction that's been disabled in a way that doesn't interfere with the visual style.
The solution suggested above works well for Windows Forms, but I see what you mean about WPF and the check mark appearing for a second.
Try this instead:
namespace System.Windows.Controls
{
public class UnchangingCheckbox : CheckBox
{
public UnchangingCheckbox()
{
this.IsReadOnly = true;
}
public bool IsReadOnly
{
get { return !this.IsHitTestVisible && !this.Focusable; }
set
{
this.IsHitTestVisible = !value;
this.Focusable = !value;
}
}
}
}
You acquire a property called "IsReadOnly", which by default is set to true, and has the behaviour you require without the annoying "checkmark appears for a second" behaviour.
I have some DomainUpDown controls in my winforms application. I cant find an option to set the default value for them on start up. Is there a way to do this?
At the moment, I have an enum type like this:
public enum ComparisonMode {Settings, Readings};
And I have set the SelectedItemChanged event of each DomainUpDown control to something like this:
private ComparisonMode ComparisonA; //enum to hold state of upDownA
private void upDownA_SelectedItemChanged(object sender, EventArgs e)
{
switch (upDownA.Text)
{
case "Settings":
ComparisonA = ComparisonMode.Settings;
break;
case "Readings":
ComparisonA = ComparisonMode.Readings;
break;
}
}
When I start the application, all the domainupdown controls have nothing selected, so user must set each of them to either Settings or Readings befor starting to work with the application.
How can I set for all of them a default value of for example (Readings)? I am thinking of looping over the controls (As they are inside a TableLayoutPanel).
I came up with this idea so far, It seems to work:
foreach (TableLayoutPanel tlp in tableCriterias.Controls)
{
foreach (Control ctrl in tlp.Controls)
{
var dud = ctrl as DomainUpDown;
if (dud != null)
{
dud.DownButton(); dud.DownButton();
//Going down 2 times to select default value
}
}
}
I do the above loop in the Load event of my form, but it makes everything very slow in the beggining since I have like 100 of these UpDown Controls.
No need to do this, you can set the SelectedIndex like this :
DomainUpDown dd = new DomainUpDown();
dd.Items.Add("settings");
dd.Items.Add("Reading");
dd.SelectedIndex = 0; // this will make sure you get the first item selected