Implementing a message suppression system with settings serialization to XML - wpf

In my application, I need a system where some messages shown to the user have a "do not show again" checkbox. In the Settings menu, I want to have a section "dismissed messages" with names of messages and a checkbox by each, so the user can un-dismiss them if needed. If the message is dismissed, the last choice user selected in the message dialog should become a default one.
Additionally, settings should be saved to an XML file - including the message suppression state and the default choice. My current solution is very crude, and I'd like to know if there is a better way.
The message class is defined as:
Public Class Message
Public Property title As String
Public Property content As String
Public Property buttons As Utilities.Enums.messageButtonTypes 'Ok/Cancel, Yes/No, etc.
Public Property allowDoNotShowAgain As Boolean = False 'whether the message is dismissable
Public Property doNotShowAgain As Boolean = False 'the actual dismiss state
Public Property result As Boolean
Public Property rememberedResult As Boolean 'last user choice if the message is dismissed
End Class
Specific messages are initialized in the MSG module:
Module Msg
'This message is not dismissable
Public connectionNotEstablished As New Message() With {
.title = "Connection not established",
.content = "Connection not established. Please check if the host application is running.",
.buttons = Utilities.Enums.messageButtonTypes.Ok
}
'This message is dismissable
Public noResultsPlotsDefined As New Message() With {
.title = "No plots defined",
.content = "You have not defined any plots. Would you like to run the study anyway?",
.buttons = Utilities.Enums.messageButtonTypes.YesNo,
.allowDoNotShowAgain = True
}
'Just a list to store references to all the messages for binding, looping, etc.
Public allMessages As New List(Of Message) From {
connectionNotEstablished,
noResultsPlotsDefined
}
Public Function ShowMessage(message As Message) As Boolean
If message.doNotShowAgain Then message.result = message.rememberedResult : Return message.rememberedResult 'If message is dismissed, return the last user choice
Dim messageDialog As New MessageDialog(message.title, message.content, message.buttons, message.allowDoNotShowAgain, message.defaultButtonCustomCaption, message.cancelButtonCustomCaption)
message.result = messageDialog.ShowDialog()
message.doNotShowAgain = messageDialog.doNotShowAgain
If message.doNotShowAgain Then message.rememberedResult = message.result
Return message.result
End Function
End Module
Specific messages are called in various functions, for example, like this:
Msg.ShowMessage(connectioNotEstablished)
So far, this is easy enough - very convenient to use while building my application. Now, the bit that I'm not sure about - the AppSettings class. Like I said, I need to store some of the properties of each message, so that I can WPF-bind to the message list in the settings window. Right now, the AppSettings has a reference to the MSG class messages list:
Public Class AppSettings
Public Property messages As List(Of Message) = Msg.allMessages
Public Sub SaveToDefaultPath()
Save(Constants.Paths.settingsFilePath)
End Sub
Private Sub Save(ByVal filename As String)
Using sw As StreamWriter = New StreamWriter(filename)
Dim xmls As XmlSerializer = New XmlSerializer(GetType(AppSettings))
xmls.Serialize(sw, Me)
End Using
End Sub
Private Function Read(ByVal filename As String) As AppSettings
Using sw As StreamReader = New StreamReader(filename)
Dim xmls As XmlSerializer = New XmlSerializer(GetType(AppSettings))
Return TryCast(xmls.Deserialize(sw), AppSettings)
End Using
End Function
End Class
In my settings WPF window, I can then bind to the messages property, and choose to show the title as TextBlock, doNotShowAgain as a CheckBox, and rememberedResult as a ComboBox. I haven't done that bit yet, but I think it should be pretty straightforward with current application architecture.
Problem is the serialization and de-serialization to and from XML (see the last two functions of the AppSettings class). Since this class stores the references to the whole messages list, which has not just title, doNotShowAgain and rememberedResult, but also the message content and it's other properties, which really clutter that XML file.
I am not sure how to solve this. I could, perhaps, store only the required variables of each message in the AppSettings, but that would require some kind of two-way converter or something. And by this point I'm starting to doubt if this is really the right way to achieve what I need.
I can't be the first one implementing this, so maybe there is a convention for such a thing. Any suggestions?
EDIT: while waiting for answers, I have successfully implemented saving the message dismiss states to XML - unfortunately, it saves the whole message class data, instead of just the title, doNotShowAgain and rememberedResult. To make this work, I had to make only one small change - the property messages in AppSettings was declared as an Array rather than as a List, so that the XML deserializer wouldn't append messages to that List, but instead, would just replace it as a whole.
Public Class AppSettings
Public Property Messages() As Message() = Msg.allMessages.ToArray()
...
End Class
So while this works (binding these messages to WPF window also works), the XML file is cluttered with unnecessary values for each message, for example:
<Message>
<title>No plots defined</title>
<content>You have not defined any plots. Would you like to run the study anyway?</content>
<buttons>YesNo</buttons>
<allowDoNotShowAgain>true</allowDoNotShowAgain>
<doNotShowAgain>false</doNotShowAgain>
<result>false</result>
<rememberedResult>false</rememberedResult>
</Message>
But for this use case, it would be more than enough to have only this bit for each message in the XML file:
<Message>
<title>No plots defined</title>
<doNotShowAgain>false</doNotShowAgain>
<rememberedResult>false</rememberedResult>
</Message>
So my question remains - what is the best solution here? Am I even in the ballpark?

It appears your only problem is having clutter in the Xml file. So you can tell the serializer to ignore certain properties with <XmlIgnore>
Public Class Message
Public Property title As String
<XmlIgnore>
Public Property content As String
<XmlIgnore>
Public Property buttons As Utilities.Enums.messageButtonTypes 'Ok/Cancel, Yes/No, etc.
<XmlIgnore>
Public Property allowDoNotShowAgain As Boolean = False 'whether the message is dismissable
Public Property doNotShowAgain As Boolean = False 'the actual dismiss state
<XmlIgnore>
Public Property result As Boolean
Public Property rememberedResult As Boolean 'last user choice if the message is dismissed
End Class
The serializer will neither serialize nor deserialize those properties.
Now you can also serialize a subset of messages defined by linq query, like this
<XmlIgnore>
Public Property messages As List(Of Message) = Msg.allMessages
<XmlElement("messages")>
Public Property messagesAllowDoNotShowAgain As List(Of Message) = Msg.allMessages.Where(Function(m) m.allowDoNotShowAgain).ToList()

Related

Winforms Data from database across multiple forms

I have a winforms applications that has an ms sql server backend. In my database i have lookup tables for like status, and other tables where the data rarely changes. In my application, several forms might use the same lookup tables (Some have a lot of data in them). Instead of loading/filling the data each time the form is open, is there a way to cache the data from the database that can be accessed from multiple forms. I did some searching, but couldnt find the best solution. There is caching, dictionaries, etc. What is the best solution and can you point me to the documentation that discusses it and may even have an example.
Edit:
In my original post I failed to mention that I have a strongly typed dataset and use tableadapters. I want to preload my lookup tables when my application starts, and then have these dataset tables be used throughout the application, on multiple forms without having to fill them on every form.
I have tried creating a class:
Public Class dsglobal
Public Shared EML_StaffingDataSet As EML_StaffingDataSet
Public Shared Sub populateDS()
EML_StaffingDataSet = New EML_StaffingDataSet
End Sub
Public Shared Sub loadskills()
Dim ta As New EML_StaffingDataSetTableAdapters.TSTAFFSKILLTableAdapter
ta.Fill(EML_StaffingDataSet.TSTAFFSKILL)
End Sub
End Class
I run this on a background worker when my application is starting up. So it loads the dataset table. On fill, I can see the datatable has data in it. When I open a form, i want to use the dataset table, but it seems to clear the data out. Not sure if my approach is correct or where my error is.
Edit2:
I have also tried this per comments, but not sure I am doing it correctly. If I am doing it correctly, then how do I use that as a datasource at design time, can i only do that programmatically?
Public Module lookupdata
Private EML_StaffingDataSet As EML_StaffingDataSet
Private skillvalues As List(Of skill)
Public ReadOnly Property skill As List(Of skill)
Get
If skillvalues Is Nothing Then
getskillvalues()
End If
Return skillvalues
End Get
End Property
Private Sub getskillvalues()
skillvalues = New List(Of skill)
EML_StaffingDataSet = New EML_StaffingDataSet
Dim ta As New EML_StaffingDataSetTableAdapters.TSTAFFSKILLTableAdapter
ta.Fill(EML_StaffingDataSet.TSTAFFSKILL)
For Each row As DataRow In EML_StaffingDataSet.TSTAFFSKILL
Dim skill As New skill
skill.skill_id = row("skill_id")
skill.skill_desc = row("skill_desc")
skill.skill_open_ind = row("skill_open_ind")
skillvalues.Add(skill)
Next
End Sub
End Module
Public Class skill
Public Property skill_id As Integer
Public Property skill_desc As String
Public Property skill_open_ind As Boolean
End Class
You might want to consider a lazy loading pattern, like this:
Public Module LookupData
Private statusValues As List(Of LookupValue)
Public Readonly Property Statuses As List(Of LookupValue)
Get
If statusValues Is Nothing Then
GetStatusValues()
End If
Return statusValues
End Get
End Property
Private Sub GetStatusValues()
statusValues = New List(Of LookupValue)
Dim sql = "select key, value from StatusTable"
'TODO: Read the items from the database here, adding them to the list.
End Sub
End Module
Public Class LookupValue
Public Property Key As String
Public Property Value As String
End Class
The idea is that you've got a single instance of LookupData (a Module in VB, there can be only one). Lookup data has a series of Properties, each of which returns a list of values from the database. If the data has already been loaded, it just returns what it has cached. If the data has not been loaded, then the first time it is referenced it retrieves it from the database.
You can consume it elsewhere in your code as follows:
Dim myStatuses = LookupData.Statuses

Alternative method to save data than my.settings for VB.Net

I need to save approximately 1600 different pieces of information for a windows forms application.
What is the best way to go about this? For example some of the user settings would look like this measuringItem1Name, measuringItem1Equation, measuringItem1Enabled, measuringItem1Offset, measuringItem2Name...
I have looked into my.settings, however, I found that it would be very tedious and error prone to type out every user setting.
It seems like the best solution would be to have a type of table that can access each individual cell and edit that information.
Does something like this exist? If so, how would it be implemented with VB.Net?
I'd recommend creating a class with properties to hold your settings. Mark the class as serializable. Then serialize and deserialize an instance of this class as application startup / shutdown to the user's profile directory. This will allow you to have strongly typed settings that save separately per user if you ever need that option. It will also allow you to easily set default values for the settings.
You can make the "settings" class as deep as you want, with properties made up of other sub setting classes or lists of settings by index. It's a very powerful pattern.
For example:
settings.MeasuringItem(1).Enabled = True
settings.MeasuringItem(1).Equation = "1+1"
settings.MeasuringItem(1).Offset = 15
settings.MeasuringItem(2).Enabled = True
settings.MeasuringItem(2).Equation = "1+1"
settings.MeasuringItem(2).Offset = 15
Settings is the main settings class with a Generic.List(of MeasuringItem) property on it. MeasuingItem is another class with .Enabled As Boolean, .Equation as string , etc properties on it.
I haven't really checked this for errors. You will want to do a lot more safety checking, but this should get you started.
<Serializable>
Public Class Settings
Public Sub New()
_measuringItems = New List(Of MeasuringItem)
End Sub
Private _measuringItems As List(Of MeasuringItem)
Public ReadOnly Property MeasuringItems As List(Of MeasuringItem)
Get
Return _measuringItems
End Get
End Property
Public Sub Save()
Using s As New IO.FileStream("Path to save", IO.FileMode.Create)
Dim formatter As New System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
formatter.Serialize(s, Me)
End Using
End Sub
Public Shared Function Load() As Settings
Dim settings As Settings = Nothing
Using s As New IO.FileStream("Path to load", IO.FileMode.Open)
Dim formatter As New System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
settings = CType(formatter.Deserialize(s), ServerHost.Settings)
End Using
Return settings
End Function
End Class
<Serializable>
Public Class MeasuringItem
Public Property Enabled As Boolean = True 'These are the default values
Public Property Equation As String = "1+1"
Public Property Offset As Integer = 15
End Class
A quick example of usage:
'Create a completely new settings instance with 100 measuring items
Dim settings1 As Settings = New Settings
For i As Integer = 1 To 100
settings1.MeasuringItems.Add(New MeasuringItem)
Next
settings1.MeasuringItems(0).Enabled = False
settings1.MeasuringItems(5).Equation = "testing"
'Save it
settings1.Save()
'load the saved settings
Dim settings2 As Settings = Settings.Load
I would recommend using a database to store the values in. I don't know your operating environment (for example, I have several enterprise SQL servers available for my use.) You could go with SQL Express Edition, which is free. SQL Lite is an option as well. You could even use Access, but that requires MS Office be installed.
If you don't want to go the database route, you can create a settings class, and make the class serializable . This will serialize the object to either a binary file or a XML file (depending on how you set it up.) If you want to go this approach, I'll post the code for you.

How to Clear WPF IDataErrorInfo Validation Errors

I am using IDataErrorInfo inherited business objects for validation.
public string UserId { get; set; }
public string this[string columnName]
{
get
{
string result = null;
if (columnName == "UserId")
{
if (string.IsNullOrEmpty(UserId))
result = "Please enter User Id";
}
}
}
I want to clear all the validation errors when I click a button on the menu - like - LogOut.
Window is making the Login panel visible but the previous panel's validation error marks are still appearing in the current login panel.
I tried all the options to assign NULL datacontext, fresh entity object...but no luck
I appreciate your help.
Using the IDataErrorInfo interface is an error-first type approach. This means that you will see the error(s) until they are cleared. You can see that there is no setter on the indexer.
The original IDataErrorInfo interface is not overly useful by itself as it only deals with one error at a time. I added the following field into my BaseDataType class:
protected ObservableCollection<string> errors = new ObservableCollection<string>();
In my actual data classes, I have the following property:
public override ObservableCollection<string> Errors
{
get
{
errors = new ObservableCollection<string>();
errors.AddUniqueIfNotEmpty(this["Property1"]);
errors.AddUniqueIfNotEmpty(this["Property2"]);
errors.AddUniqueIfNotEmpty(this["PropertyN"]);
return errors;
}
}
The AddUniqueIfNotEmpty method is an extension method which I believe is self-explanatory. This property calls the indexer any number of times and compiles all of the results into a ObservableCollection<string> collection ready to be displayed in the UI. You'll need to call the INotifyPropertyChanged.PropertyChanged event with the name Errors when Property1, Property2 and PropertyN are updated to make this work.
You could do something like this, but add a setter for you to pass in an empty collection or string when you want to clear the errors.

Data Validation with MVVM-Light WPF and Linq to Entity Framework

I think I have read every article google returns when I search wpf mvvm-light data validation and I dont know which way to go. I am aware of josh smith, Karl Shifflett's, and MVVM LIGHT's own demo techniques for data validation. What I see is that most validation requires me to fully "re-abstract" my model in my view model. Meaning that I have to create a property in my viewmodel for each property of my model that I want to validate (and in some cases convert all these into string values for binding/validation). This seems like a lot or redundancy when all I want to do is mark most fields as required.
I am using LINQ to entity framework(with self tracking) for my model classes which come from a SQL server DB. As a result I would prefer to keep my business data validation/rules within my viewmodels. I write a simple service interface to get the data from the model and pass it to my viewmodel.
Most of the examples I can find are from as far back as 2008 (ie josh smith). Are these techniques still valid or are there more up to date best practices for mvvm data validation with .NET 4.5 etc.
So I am asking:
1) What methods do you suggest I use
2) What methods work best in a LINQ to EF with MVVM-Light Environment.
3) EDIT: I want to provide feedback to user as they enter data, not just when they submit form
thanks
The way I do this (not necessarily correct) is to do my validation in the ViewModel (where the CRUD operations typically take place), then if there are validation errors, abort saving/adding any data and use Messenger.Default.Send to send a custom message type to my view. Then I alert the user via a DialogBox or otherwise.
I've experimented with Binding ValidationRules in the past but found by far the most reliable and consistent method to be simple if statements.
I eventually ended up using the following. I changed my model to use LINQ to self tracking entities (see this article for info about STE http://msdn.microsoft.com/en-us/library/vstudio/ff407090%28v=vs.100%29.aspx).
LINQ to STE creates an OnPropertyChanged event that implements the iNotifyPropertyChanged interface.
I just created a public partial class for the matching model object (linq entity generated code) I wanted and added an event handler for the OnPropertyChanged event. I then used the IDataErrorInfo interface to validate and throw errors as I needed. This allows me to validate the fields as they change which gets reflected to the user. This also allows you to perform more advanced validation logic that may need to requery the database (i.e. to look for if a username is already used etc.) or throw a dialog box
Also, having the data validation in the model allows me to still have validation if i perform direct "batch" operations that bypass the UI.
I then used an HasErrors and HasChanges property and used them to create a Boolean value that gets attached to the relay commands, disabling the crud command buttons if errors are present.
I will post some simple code to outline what I just described, comment if you want more detail.
Here is the Entity Framework extension of the model class:
Imports System.ComponentModel
Partial Public Class client
Implements IDataErrorInfo
#Region "Properties / Declarations"
'Collection / error description
Private m_validationErrors As New Dictionary(Of String, String)
Private _HasChanges As Boolean = False
''Marks object as dirty, requires saving
Public Property HasChanges() As Boolean
Get
Return _HasChanges
End Get
Set(value As Boolean)
If Not Equals(_HasChanges, value) Then
_HasChanges = value
OnPropertyChanged("HasChanges")
End If
End Set
End Property
'Extends the class with a property that determines
'if the instance has validation errors
Public ReadOnly Property HasErrors() As Boolean
Get
Return m_validationErrors.Count > 0
End Get
End Property
#End Region
#Region "Base Error Objects"
'Returns an error message
'In this case it is a general message, which is
'returned if the list contains elements of errors
Public ReadOnly Property [Error] As String Implements System.ComponentModel.IDataErrorInfo.Error
Get
If m_validationErrors.Count > 0 Then
Return "Client data is invalid"
Else
Return Nothing
End If
End Get
End Property
Default Public ReadOnly Property Item(ByVal columnName As String) As String Implements System.ComponentModel.IDataErrorInfo.Item
Get
If m_validationErrors.ContainsKey(columnName) Then
Return m_validationErrors(columnName).ToString
Else
Return Nothing
End If
End Get
End Property
#End Region
#Region "Base Error Methods"
'Adds an error to the collection, if not already present
'with the same key
Private Sub AddError(ByVal columnName As String, ByVal msg As String)
If Not m_validationErrors.ContainsKey(columnName) Then
m_validationErrors.Add(columnName, msg)
End If
End Sub
'Removes an error from the collection, if present
Private Sub RemoveError(ByVal columnName As String)
If m_validationErrors.ContainsKey(columnName) Then
m_validationErrors.Remove(columnName)
End If
End Sub
#End Region
Public Sub New()
Me.HasChanges = False
End Sub
#Region "Data Validation Methods"
''handles event and calls function that does the actual validation so that it can be called explicitly for batch processes
Private Sub ValidateProperty(ByVal sender As Object, ByVal e As PropertyChangedEventArgs) Handles Me.PropertyChanged
If e.PropertyName = "HasChanges" Then
Exit Sub
End If
IsPropertyValid(e.PropertyName)
HasChanges = True
End Sub
Public Function IsPropertyValid(sProperty As String) As Boolean
Select Case sProperty
''add validation by column name here
Case "chrLast"
If Me.chrLast.Length < 4 Then
Me.AddError("chrLast", "The last name is too short")
Return True
Else
Me.RemoveError("chrLast")
Return False
End If
Case Else
Return False
End Select
End Function
#End Region
End Class
then in the view model I included the following code to bind thecommand and evaluate whether or not it can be executed.
Public ReadOnly Property SaveCommand() As RelayCommand
Get
If _SaveCommand Is Nothing Then
_SaveCommand = New RelayCommand(AddressOf SaveExecute, AddressOf CanSaveExecute)
End If
Return _SaveCommand
End Get
End Property
Private Function CanSaveExecute() As Boolean
Try
If Selection.HasErrors = False And Selection.HasChanges = True Then
Return True
Else
Return False
End If
Catch ex As Exception
Return False
End Try
End Function
Private Sub SaveExecute()
''this is my LINQ to Self Tracking Entities DataContext
FTC_Context.SaveChanges()
End Sub
the following is how I bound my button (has custom styling in WPF)
<Button Content="" Height="40" Style="{DynamicResource ButtonAdd}" Command="{Binding SaveCommand}" Width="40" Cursor="Hand" ToolTip="Save Changes" Margin="0,0,10,10"/>
so, when there are no validation errors and the current client record "isDirty" the save button automatically becomes enabled, and disabled if any of those two conditions fail. This way I now have a simple way of validating any type of column/data I want for the entity, and I can provide user feedback as they enter data in the form, and only enable CRUD command buttons once all my "conditions" have been met.
This was quite a battle to figure out.

WPF Custom Control and exposing properties thru DependencyProperty

Ok - I'm pulling my hair out over what I thought was a simple scenario: create a custom Label for bilingual use that contained two additional properties (EnglishText, FrenchText). Currently its structured like this:
Public Class myCustomLabel
Inherits System.Windows.Controls.Label
Public myEnglishTextProperty As DependencyProperty = DependencyProperty.Register("myEnglishText", GetType(String), GetType(myCustomLabel), New PropertyMetadata("English", New PropertyChangedCallback(AddressOf TextChanged)))
Public myFrenchTextProperty As DependencyProperty = DependencyProperty.Register("myFrenchText", GetType(String), GetType(myCustomLabel), New PropertyMetadata("Francais", New PropertyChangedCallback(AddressOf TextChanged)))
Public Sub New()
'This OverrideMetadata call tells the system that this element wants to provide a style that is different than its base class.
'This style is defined in themes\generic.xaml
DefaultStyleKeyProperty.OverrideMetadata(GetType(myCustomLabel), New FrameworkPropertyMetadata(GetType(myCustomLabel)))
End Sub
Public Property myEnglishText() As String
Get
Return MyBase.GetValue(myFrenchTextProperty)
End Get
Set(ByVal value As String)
MyBase.SetValue(myFrenchTextProperty, value)
End Set
End Property
Public Property myFrenchText() As String
Get
Return MyBase.GetValue(myFrenchTextProperty)
End Get
Set(ByVal value As String)
MyBase.SetValue(myFrenchTextProperty, value)
End Set
End Property
Private Sub TextChanged(ByVal d As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)
If DesignerProperties.GetIsInDesignMode(Me) = True Then
Me.Content = myEnglishText
Else
If myUser.Language = "E" Then
Me.Content = myEnglishText
Else
Me.Content = myFrenchText
End If
End If
End Sub
End Class
My test window grid xaml is simple:
<Grid>
<my:myCustomLabel myEnglishText="English Text" myFrenchText="English Text" Height="25" Width="100" Background="Aqua" Foreground="Black"/>
</Grid>
This seems to work in the development environment - changing the English and French texts change the in the design preview and it works when the app runs and the test window is opened. But only the first time - if I open the test window a second time I receive the following message:
'myEnglishText' property was already
registered by 'myCustomLabel'.
I understand now that if I change the dependency property declarations to shared then this problem goes away - but that leads to a host of other problems like the callback function being required to be shared as well - and thus unable to update the Content (which needs to be instantiated with the class). All I really want is the content property to be updated in design time when the english and french labels are changed.
Is there a way around this? Or maybe are dependency properties overkill for what I need?
You are registering your dependency properties as instance variables, and during the instance constructor. So they are getting registered again every time you instantiate the control, which causes an error the second time. As you have found out, dependency properties need to be static (Shared) members:
Public Shared myEnglishTextProperty As DependencyProperty =
DependencyProperty.Register("myEnglishText", GetType(String), GetType(myCustomLabel),
New PropertyMetadata("English", New PropertyChangedCallback(AddressOf TextChanged)))
You probably need to call OverrideMetadata in your shared constructor (type initialiser) rather than your instance constructor as well.
Regarding your issue with the callback needing to be shared: yes, it will be, but one of the arguments to the callback is the label instance. So you can just cast that to label and call an instance method on that:
private static void TextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((MyLabel)d).TextChanged();
}
private void TextChanged()
{
// your code here
}
(forgive C# syntax)
Is the reason you don't want the callback method to be shared because you're accessing the "me" instance? If that's all it is, make it shared and use the "d" parameter. I don't know VB well enough to show you the code, but just create a variable of type myCustomLabel and assign "d" to it (with a cast). Then use that variable (say "lbl") instead:
If DesignerProperties.GetIsInDesignMode(lbl) = True Then
lbl.Content = myEnglishText
Else
If myUser.Language = "E" Then
lbl.Content = myEnglishText
Else
lbl.Content = myFrenchText
End If
End If
Also, there's a slight bug in your example code. Try using this:
Public Property myEnglishText() As String
Get
Return MyBase.GetValue(myEnglishTextProperty)
End Get
Set(ByVal value As String)
MyBase.SetValue(myEnglishTextProperty, value)
End Set
End Property
Instead of this:
Public Property myEnglishText() As String
Get
Return MyBase.GetValue(myFrenchTextProperty)
End Get
Set(ByVal value As String)
MyBase.SetValue(myFrenchTextProperty, value)
End Set
End Property

Resources