I have a Combobox and I want to edit a boolean value.
<dxe:ComboBoxEdit ItemsSource="{Binding EnumItemsSource}"
DisplayMember="Name"
ValueMember="Id"
IsTextEditable="False"
EditValue="{Binding TargetValue, UpdateSourceTrigger=PropertyChanged}"/>
My ViewModel:
/// <summary>
/// Contains the ItemsSource for Enums
/// </summary>
public List<EnumItemObject> EnumItemsSource
{
get { return _enumItemsSource; }
set
{
_enumItemsSource = value;
OnPropertyChanged();
}
}
public class EnumItemObject
{
public int Id {get;set;}
public string Name {get;set;}
}
And I prepare the data for the Combobox ItemsSource that:
/// <summary>
/// Sets the value to the properties for the BitTemplate view. (similar with EnumTemplate)
/// </summary>
/// <param name="propertyInfo">a boolean property</param>
private void PrepareDataForBitTemplate(PropertyInfo propertyInfo)
{
TargetValue = (int)propertyInfo.GetValue(_firstSelectedItem);
EnumItemsSource = new List<EnumItemObject>();
EnumItemsSource.Add(new EnumItemObject() { Id = 0, Name = "Nein" });
EnumItemsSource.Add(new EnumItemObject() { Id = 1, Name = "Ja" });
}
Is it the approach correct? Any solution easier?
Thanks
More natural way in WPF is to use a Value Converter. In short it's an object that manipulates how the values of a property are bound to the UI object.
In your case that means (just for example - there are certainly more approaches) creating a converter which would convert the boolean to 0 or 1 and you can then bind that property to the SelectedIndex of a ComboBox with pre-defined text Nein/Ja values (on the correct indexes) with that converter.
An unrelated thing: if you're developing your app with GUI text in another language than English, I would consider moving it to a resources file with English names for the keys - to make the code more accessible to people not speaking German for example. It may be the case you don't need it, I just felt I should mention it :)
Related
I have a file named App.xml that is included in my installer(will be installed on the client computer) which I want to load data from and display it to the user so he can manipulate what will be installed/ how to use the system.
I've tried using Xml Files extensions/ custom actions, checked online, couldn't find a way to load a source file embedded in the installer.
my file is:
<App> <Text>bla bla</text></App>
I want the installer to show the "bla bla" text and the user can change it which can later be saved through an extension as usual..
thanks!
I've run into a similar problem. Digging around for a solution that allows you to use an XML file during the InstallUISequence, before files are installed, it looks like you need to add a custom table to your WiX definition as described here.
Basically, you create a CustomTable element in your .wxs file, for your example something like:
<CustomTable Id="App">
<Text>bla bla</Text>
</CustomTable>
Then you can read from it in a c# custom action by creating a View query to find the desired properties:
using (View view = session.Database.OpenView("SELECT 'Text' FROM 'App'"))
{
view.Execute();
// access view properties and turn them into some object you want to manipulate
}
I'll admit some ignorance as to what that view object is going to have, but I know you can iterate through its records or grab individual columns, poking around in the properties should eventually find you the values you want.
Next step is to populate a combobox element with the values
<Control Id="DropdownSelectLabel" Type="Text" X="50" Y="65" Width="200" Height="15" TabSkip="no" Text="&Select a value:">
</Control>
<Control Id="DropdownSelect" Type="ComboBox" Height="16" Width="200" X="60" Y="80" Property="MY_PROPERTY_KEY" ComboList="yes">
<ComboBox Property="MY_PROPERTY_KEY">
<!-- Optional prepopulate value-->
<ListItem Text="[dummy_text]" Value="[dummy_value]" />
</ComboBox>
</Control>
I'm populating it with a custom c# action running during the InstallUISequence, built through visual studio
<!-- Custom action for populating the combobox -->
<CustomAction Id="CA_PopulateComboBox" BinaryKey="BIN_CustomActions" DllEntry="PopulateComboBox" Execute="firstSequence" />
<!-- Binaries for the custom action -->
<Binary Id="BIN_CustomActions" SourceFile="..\PATH-TO-YOUR-CUSTOM-ACTION-BIN-RELEASE.CA.dll" />
<!-- Schedule the custom action -->
<InstallUISequence>
<Custom Action="CA_PopulateComboBox" Before="LaunchConditions" />
</InstallUISequence>
Custom action looks like this:
public class CustomActions
{
/// <summary>
/// Populates the ComboBox UI Element.
/// </summary>
/// <param name="session">The session.</param>
[CustomAction]
public static void PopulateComboBox(Session session)
{
session.Log("Populating the combobox with certificates");
// Clear the combobox (unecessary if it starting empty)
View view = session.Database.OpenView("DELETE FROM ComboBox WHERE ComboBox.Property='MY_PROPERTY_KEY'");
view.Execute();
view = session.Database.OpenView("SELECT * FROM ComboBox");
view.Execute();
List<ComboBoxRecordWrapper> valuesToAdd = PopulateValuesObjects(session); // Add the logic to read your xml values from the session object here
var index = 1;
foreach (ComboBoxRecordWrapper valueObject in valuesToAdd)
{
session.Log($"Adding value to the combobox: {valueObject.Text} - {valueObject.Value} {Environment.NewLine}Order: {valueObject.Order}");
view.Modify(ViewModifyMode.InsertTemporary, recordWrapper.ToRecord());
view.Execute();
index++;
}
view.Close();
}
}
/// <summary>
/// Class ComboBoxRecordWrapper. Wraps objects that should be represented in a combobox element in the installer
/// </summary>
public class ComboBoxRecordWrapper
{
/// <summary>
/// Gets or sets the property that this element's value will be stored as if the element is selected
/// </summary>
/// <value>The property.</value>
public string Property { get; set; }
/// <summary>
/// Gets or sets the order that this element appears in the combobox
/// </summary>
/// <value>The order.</value>
public int Order { get; set; }
/// <summary>
/// Gets or sets the value of the combobox option. This is what will be available to the UI element as a returned value
/// </summary>
/// <value>The value.</value>
public string Value { get; set; }
/// <summary>
/// Gets or sets the text that will be displayed for this element
/// </summary>
/// <value>The text.</value>
public string Text { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="ComboBoxRecordWrapper"/> class.
/// </summary>
/// <param name="property">The property.</param>
/// <param name="order">The order.</param>
/// <param name="value">The value.</param>
/// <param name="text">The text.</param>
public ComboBoxRecordWrapper(string property, int order, string value, string text)
{
this.Property = property;
this.Order = order;
this.Value = value;
this.Text = string.IsNullOrEmpty(text) ? value : text;
}
/// <summary>
/// Converts to a record to add to the MSI database.
/// </summary>
/// <returns>Record.</returns>
public Record ToRecord()
{
var record = new Record(4);
record.SetString(1, this.Property);
record.SetInteger(2, this.Order);
record.SetString(3, this.Value);
record.SetString(4, this.Text);
return record;
}
}
This would be custom functionality that you would have to provide through a custom action. From a high level design I see you passing in the path to the xml file on the command line. At run time your custom action would read the nodes in the file and set the appropriate properties that your text boxes are using.
I'm building an MVVM Light WPF app in Visual Studio 2015 with Entity Framework 6 (EF) providing the data. I have a ComboBox that displays the reasons why someone needs to take a drug test and it looks like this:
<ComboBox ItemsSource="{Binding ReasonsForTest}"
SelectedItem="{Binding Path=ReasonsForTestVm,
UpdateSourceTrigger=PropertyChanged}"
DisplayMemberPath="Description" />
The ReasonsForTest is of type ReasonForTestViewModel class:
public class ReasonForTestViewModel: ViewModelBase
{
private int _ReasonForTestId;
private string _ReasonForTestAbbr;
private string _description;
public int ReasonForTestId
{
get { return _ReasonForTestId; }
set
{
if (value == _ReasonForTestId) return;
_ReasonForTestId = value;
RaisePropertyChanged();
}
}
public string ReasonForTestAbbr
{
get { return _ReasonForTestAbbr; }
set
{
if (value == _ReasonForTestAbbr) return;
_ReasonForTestAbbr = value;
RaisePropertyChanged();
}
}
public string Description
{
get { return _description; }
set
{
if (value == _description) return;
_description = value;
RaisePropertyChanged();
}
}
}
I have a data service class that contains the following code to fetch the data for the valid values of the ComboBox:
public async Task<ObservableCollection<ReasonForTestViewModel>> GetReasonsForTest()
{
using (var context = new MyEntities())
{
var query = new ObservableCollection<ReasonForTestViewModel>
(from rt in context.ReasonForTests
orderby rt.description
select new ReasonForTestViewModel
{
ReasonForTestId = rt.ReasonForTestID,
ReasonForTestAbbr = rt.ReasonForTestAbbr,
Description = rt.description,
});
return await Task.Run(() => query);
}
}
The view model populates the ComboBox using this:
var dataService = new TestDataService();
ReasonsForTest = await dataService.GetReasonsForTest();
The ComboBox has the correct data; however, it's not selecting the correct value when the app starts -- it's showing blank on load. The SelectedItem (ReasonsForTestVm) is also of that class type ReasonForTestViewModel and gets populated from the database with the one item for this person. I've stepped through the code to ensure ReasonsForTestVm has the correct data, and it does.
Here's the property for ReasonsForTestVm:
public ReasonForTestViewModel ReasonForTestVm
{
get
{
return _reasonForTestVm;
}
set
{
if (Equals(value, _reasonForTestVm)) return;
_reasonForTestVm = value;
RaisePropertyChanged();
}
}
What am I doing wrong here? I'm about to lose my mind!
Update: Sorry for the confusing name in the property above. Fixed.
Any WPF items control that extends Selector (such as ComboBox and ListBox) has two properties that are often used in conjunction: ItemsSource and SelectedItem.
When you bind a collection to ItemsSource, a representation of those items are shown in the UI. Each one of the representations is bound to an instance found within the collection bound to ItemsSource. If, for an example, you're using a DataTemplate to create that representation, you'll find within each that the DataContext will be one of those instances from the collection.
When you select one of these representations, the SelectedItemproperty now holds the instance from the collection that was bound to that representation.
This works perfectly through user interaction with the UI. However, there's one important caveat when interacting with these controls programmatically.
It's a very common pattern to bind these properties to similar properties in your view model.
public class MuhViewModel
{
public MuhItems[] MuhItems {get;} = new[]{ new Item(1), new Item(2) };
// I don't want to show INPC impls in my sample code, kthx
[SuperSlickImplementINotifyPropertyChangedAttribute]
public MuhSelectedItem {get;set;}
}
bound to
<ComboBox ItemsSource="{Binding MuhItems}"
SelectedItem="{Binding MuhSelectedItem}" />
If you try to manually update the selected item this way...
muhViewModel.MuhSelectedItem = new Item(2);
The UI will not change. The Selector sees that ItemsSource has changed, yes, but it doesn't find that instance in the ItemsSource collection. It doesn't know that one instance of Item with a value of 2 is equivalent to any other Item with the same value. So it does nothing. (That's a bit simplistic for what really happens. You can bust out JustDecompile and see for yourself. It gets real convoluted down in there.)
What you should be doing in this situation is updating SelectedItem with an instance found within the collection bound to ItemsSource. In our example,
var derp = muhViewModel.MuhItems.FirstOrDefault(x => x.MuhValue == 2);
muhViewModel.MuhSelectedItem = derp;
Side note, when tracking instances within a debug session, it helps to use Visual Studio's Make Object ID feature.
I have the following situation - I need to write a custom additional metadata attribute, that based on another property value (from the same model), adds a value to the AdditionalValues dictionary. Right now, my issue is that I'm not able to access the model instance inside my attribute class.
[AttributeUsage(AttributeTargets.Property)]
public class ExtendedAdditionalMetadataAttribute : Attribute, IMetadataAware
{
#region Private properties
private string extraFieldToCheck { get; set; }
private string extraFieldValueToCheck { get; set; }
private string fieldToBeAdded { get; set; }
private string fieldValueToBeAdded { get; set; }
#endregion
#region Constructor
public ExtendedAdditionalMetadataAttribute(string extraFieldToCheck, string extraFieldValueToCheck,
string fieldToBeAdded, string fieldValueToBeAdded)
{
this.extraFieldToCheck = extraFieldToCheck;
this.extraFieldValueToCheck = extraFieldValueToCheck;
this.fieldToBeAdded = fieldToBeAdded;
this.fieldValueToBeAdded = fieldValueToBeAdded;
}
#endregion
public void OnMetadataCreated(ModelMetadata metadata)
{
// HOW TO GET THE MODEL CLASS INSTANCE???
// metadata.ContainerType is correct by metadata.Container is null.
}
}
As you see from the code comments, inside OnMetadataCreated I need to access the Model class instance but, though ContainerType is correct, the Container property is NULL.
Can you please help me by giving me a hint regarding this issue?
THANK YOU IN ADVANCE!
Evdin
LATER EDIT
Considering that I haven't gave to much explanations, I will also paste here an example on how I would like to use this attribute on a model class:
/// <summary>
/// Gets or sets the IsAccountCreated
/// </summary>
/// <value>The IsAccountCreated.</value>
[UIHint("FormFieldStringTemplate")]
[ExtendedAdditionalMetadata("IsExternalAccount", "true", "ReadOnly", "true")]
public override Boolean IsAccountCreated { get; set; }
/// <summary>
/// Gets or sets the IsAccountEnabled
/// </summary>
/// <value>The IsAccountEnabled.</value>
[Display(Name = "Este cont activ?")]
[UIHint("FormFieldStringTemplate")]
[ExtendedAdditionalMetadata("IsExternalAccount", "true", "ReadOnly", "true")]
public override Boolean IsAccountEnabled { get; set; }
/// <summary>
/// Gets or sets the IsExternalAccount
/// </summary>
/// <value>The IsExternalAccount.</value>
[Display(Name = "Este cont extern?")]
[UIHint("FormFieldStringTemplate")]
[AdditionalMetadata("ReadOnly", "true")]
public override Boolean IsExternalAccount { get; set; }
Later & Later Edit
Though the response given by #stephen-muecke is more then simple and acceptable in current situation, for the sake of programming challenge I've looked for other options and I found the following possibility: implementing a custom DataAnnotationsModelMetadataProvider class. In few simple words - it works and I'm able to obtain the model class instance BUT only if the model class is a simple class, otherwise there are many drawbacks - for example if you have a Model class and you use it in your view then it's ok but if you have a class inside another class (a model inside a viewmodel) that this approach is not usable anymore.
Thank you again #stephen-muecke!
Since you seem to need access to multiple properties of the model, the attribute should target class (AttributeTargets.Class) and be applied to the model, not a property. This might mean you need to add another property that is the name of the property you were trying to apply this to. Note metadata.ContainerType only gives you the type, not this instance so you can only get the default value of its properties.
Edit
If the attributes need to be applied to multiple properties in the model, then you cannot access the container in OnMetadataCreated because metadata is created from the innermost properties out so the model's metadata has not yet been created.
Based on OP's comments, a better solution would be to create a custom html helper. For example to generate a textbox that is readonly based on the value of another property
namespace MyHelpers.Html
{
public static class ReadOnlyHelpers
{
public static MvcHtmlString ReadOnlyTextBoxIf<TModel, TValue>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TValue>> expression, bool isReadOnly)
{
object attributes = isReadOnly ? new { #readonly = "readonly" } : null;
return InputExtensions.TextBoxFor(helper, expression, attributes);
}
}
}
and use in your view as
#Html.ReadOnlyTextBoxIf(m => m.SomeTextProperty, Model.SomeBooleanValue)
Creating a 'Readonly' checkbox is a little more difficult because the readonly attribute has no affect with a checkbox. In order to prevent user interaction you need to disable it but that means the value wont post back
public static MvcHtmlString ReadOnlyCheckBoxIf<TModel>(this HtmlHelper<TModel> helper, Expression<Func<TModel, bool>> expression, bool isReadOnly)
{
if (isReadOnly)
{
// If you want to 'visually' render a checkbox (otherwise just render a div with "YES" or "NO")
ModelMetadata metaData = ModelMetadata.FromLambdaExpression(expression, helper.ViewData);
StringBuilder html = new StringBuilder();
// Add a hidden input for postback
html.Append(InputExtensions.HiddenFor(helper, expression).ToString());
// Add a visual checkbox without name so it does not post back
TagBuilder checkbox = new TagBuilder("input");
checkbox.MergeAttribute("type", "checkbox");
checkbox.MergeAttribute("disabled", "disabled");
if ((bool)metaData.Model)
{
checkbox.MergeAttribute("checked", "checked");
}
html.Append(checkbox.ToString());
return MvcHtmlString.Create(html.ToString());
}
else
{
// return normal checkbox
return InputExtensions.CheckBoxFor(helper, expression);
}
}
and use in your view as
#Html.ReadOnlyCheckBoxIf(m => m.IsAccountCreated, Model.IsExternalAccount)
I have a model with properties:
/// <summary>
/// List of available map modes
/// </summary>
public Array MapModes { get; private set; }
/// <summary>
/// The current cartographic mode of the map
/// </summary>
public MapCartographicMode MapMode
{
get { return _mapMode; }
set
{
if (value == _mapMode) return;
_mapMode = value;
OnPropertyChanged();
}
}
/// <summary>
/// List of available map color modes
/// </summary>
public Array MapColorModes { get; private set; }
//The current color mode of the map
public MapColorMode MapColorMode
{
get { return _mapColorMode; }
set
{
if (value == _mapColorMode) return;
_mapColorMode = value;
OnPropertyChanged();
}
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
And My XAML looks like this:
<maps:Map x:Name="MainMap"
Height="{Binding MapHeight}"
ColorMode="{Binding MapColorMode, Converter={StaticResource MapTestConverter}}">
The properties are updated on another page.
<toolkit:ListPicker Header="Map mode"
ItemsSource="{Binding MapModes}"
SelectedItem="{Binding Path=MapMode, Mode=TwoWay}"/>
<toolkit:ListPicker Header="Map color mode"
ItemsSource="{Binding MapColorModes}"
SelectedItem="{Binding Path=MapColorMode, Mode=TwoWay}"/>
Now the binding of the ListPickers works fine the value in the model always represents what was last picked here.
The Map binding also works, it gets the initial value and also updates the first time I change a property.
BUT that's it. after the first property change it refuses to update. (The dummy IValueConverter isn't called).
The model still nicely raises Property changed events, and the property has the correct value in the model (manually assigning it for example at page load works flawlessly)
Since it seems the Binding is getting "broken" I tried recreating it each time the property was updated
Binding b = new Binding("MapMode");
BindingOperations.SetBinding(MainMap, Map.CartographicModeProperty, b);
This works. I am beginning to think there is a bug or something in the wp8 map implementation. (Or I may just miss something completely obvious^^)
I had the same.
The solution is to provide Mode=TwoWay to the bindings. I have no idea why that works, but it seems.
On this blog I have seen exactly this solution:
http://dotnetbyexample.blogspot.ch/2012/10/introducing-new-windows-8-map-control.html
I'm trying to localise a WinForms app for multiple languages. I'm trying to find a way to set my form labels/buttons text properties to read from the resources file in the designer (rather than having to maintain a chunk of code that sets them programatically).
I've found I can set form.Localizable=true, but then the resources are read from a file alongside the form, but many of mine are shared across multiple forms.
Is there any way to set a label's text in the designer, to a value stored in a project-level resx file?
I think I found a way to do this!
First in your Resources.resx set the Access Modifier to Public.
After that in the designer generated code (Form.Designer.cs) you can write this to the appropriate control:
this.<control>.Text = Properties.Resources.<stringname>
for example:
this.footerLabel.Text = Properties.Resources.footerString;
ps.:I don't know how ethical this solution is, but it works!
To answer the question, no.
But IMO, this should not be done anyways if the text will be static.
Have a read at my answers on localization and resources:
Resource string location
Globalize an existing Windows Forms application
Using .resx files for global application messages
Easy enough to implement, by the way, this can be done for any type of control you like to bind to a resource, or any other class. I do this for static classes like my application settings as well.
Entering code like this:
textBox2.DataBindings.Add("Text", source, "<className>.<PropertyName>");
is not giving me a "good feeling", never mind the spelling
Here is a litle sample of the above label that provides a dropdown on the resources of a application.
First the control, contains 1 new property named ResourceName
the magic comes from the editor, this one is specified in the annotation above the property and is called ResourceDropDownListPropertyEditor
[Editor(typeof(ResourceDropDownListPropertyEditor), typeof(System.Drawing.Design.UITypeEditor))]
The code for the label class:
/// <summary>
/// Label bound to resource
/// </summary>
/// <remarks>
/// The bitmap does not appear in the Toolbox for autogenerated controls and components.
/// https://learn.microsoft.com/en-us/dotnet/framework/winforms/controls/how-to-provide-a-toolbox-bitmap-for-a-control</remarks>
/// <seealso cref="System.Windows.Forms.Label" />
[ToolboxBitmap(typeof(Label))]
public partial class ResourceLabel : Label
{
/// <summary>
/// backing field for the resource key property
/// </summary>
private string mResourceName;
[Browsable(true)]
[DefaultValue("")]
[SettingsBindable(true)]
[Editor(typeof(ResourceDropDownListPropertyEditor), typeof(System.Drawing.Design.UITypeEditor))]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[Description("Select the resource key that you would like to bind the text to.")]
public string ResourceName
{
get { return mResourceName; }
set
{
mResourceName = value;
if (!string.IsNullOrEmpty(mResourceName))
{
base.Text = Properties.Resources.ResourceManager.GetString(mResourceName);
}
}
}
/// <summary>
/// Designer helper method: https://msdn.microsoft.com/en-us/library/ms973818.aspx
/// </summary>
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
private bool ShouldSerializeResourceName()
{
return !string.IsNullOrEmpty(ResourceName);
}
/// <summary>
/// Will be default text if no resource is available
/// </summary>
[Description("default text if no resource is assigned or key is available in the runtime language")]
public override string Text
{
get { return base.Text; }
set
{
// Set is done by resource name.
}
}
}
Here is the class used for the drop down:
/// <summary>
/// used for editor definition on those properties that should be able
/// to select a resource
/// </summary>
/// <seealso cref="System.Drawing.Design.UITypeEditor" />
class ResourceDropDownListPropertyEditor : UITypeEditor
{
IWindowsFormsEditorService _service;
/// <summary>
/// Gets the editing style of the <see cref="EditValue"/> method.
/// </summary>
/// <param name="context">An ITypeDescriptorContext that can be used to gain additional context information.</param>
/// <returns>Returns the DropDown style, since this editor uses a drop down list.</returns>
public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
{
// We're using a drop down style UITypeEditor.
return UITypeEditorEditStyle.DropDown;
}
/// <summary>
/// Displays a list of available values for the specified component than sets the value.
/// </summary>
/// <param name="context">An ITypeDescriptorContext that can be used to gain additional context information.</param>
/// <param name="provider">A service provider object through which editing services may be obtained.</param>
/// <param name="value">An instance of the value being edited.</param>
/// <returns>The new value of the object. If the value of the object hasn't changed, this method should return the same object it was passed.</returns>
public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
{
if (provider != null)
{
// This service is in charge of popping our ListBox.
_service = ((IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService)));
if (_service != null)
{
var items = typeof(Properties.Resources).GetProperties()
.Where(p => p.PropertyType == typeof(string))
.Select(s => s.Name)
.OrderBy(o => o);
var list = new ListBox();
list.Click += ListBox_Click;
foreach (string item in items)
{
list.Items.Add(item);
}
if (value != null)
{
list.SelectedValue = value;
}
// Drop the list control.
_service.DropDownControl(list);
if (list.SelectedItem != null && list.SelectedIndices.Count == 1)
{
list.SelectedItem = list.SelectedItem.ToString();
value = list.SelectedItem.ToString();
}
list.Click -= ListBox_Click;
}
}
return value;
}
private void ListBox_Click(object sender, System.EventArgs e)
{
if (_service != null)
_service.CloseDropDown();
}
}
In the end what you get will look like this at design-time:
The resource names are created when you drop the control on your form, changes are not seen till you re-compile and close/open the form or drop a new label on the form.
The only way I can think of would be to create a custom control that would add a property for the resource name. When the property is set, grab the value from the project resource file and set the text property with it. You will want to make sure that Text doesn't get serialized or it might overwrite the value set by ResourceName.
public class ResourceLabel
: Label
{
private string mResourceName;
public string ResourceName
{
get { return mResourceName; }
set
{
mResourceName = value;
if (!string.IsNullOrEmpty(mResourceName))
base.Text = Properties.Resources.ResourceManager.GetString(mResourceName);
}
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public override string Text
{
get { return base.Text; }
set
{
// Set is done by resource name.
}
}
}
I have just been looking at this very thing.
If you own the control, ie it is your own custom control, you can use CodeDOM
Read this article for some background and download this example to see how it's done.
In our app we need to replace placeholders with "DisplayText" form the database.
So we have Text properties like "Order {Product}" and we want to replace with GetDisplayText("Order {Product}")`.
So in order to do this I have added the following code:
statements.OfType<CodeAssignStatement>()
.Where(s => s.Left is CodePropertyReferenceExpression && ((CodePropertyReferenceExpression)s.Left).PropertyName == "Text")
.ToList().ForEach(s =>
{
s.Right = new CodeMethodInvokeExpression(
new CodeMethodReferenceExpression(new CodeTypeReferenceExpression("Core.DisplayText"), "GetDisplayText"),
s.Right);
});
However I am still experimenting with it and I haven't created a working solution yet... But it may help you.
:-)