Winforms: Enabling Localization by default (enforcing a project/solution policy) - winforms

Is there an easy way to set the Localizable property to true for newly created usercontrols / forms? The scope of the setting should ideally be a solution or a project.
In other words I want to say that this project/solution should be localizable, and then if I add a new form or control VS should automatically set the property to true.
Edit:
Although custom templates are possible, in a larger team they might not be always used. So it's more about enforcing a policy, ensuring that the team members do not ommit to set the property for the projects/solutions where it is a requirement that all forms/controls containing text resources should be localizable.
Note: Team Foundation Server is not an Option.

Not sure if it is worth the effort for a property that is so easy to change and so easy to see that it has the wrong value. But you can create your own item template.
For example: Project + Add User Control. Set its Localizable property to True. File + Export Template, select Item template. Next. Check the control you added. Next. Check all references, omit only the ones you'll never need. Next. Give it is good template name (say: "Localizable User Control").
You'll now have an item template available for future projects that has the property set. Repeat as necessary for other item templates, like a Form.

It's possible to write a unit test that uses reflection to determine whether a form/user control has been marked as localizable. Specifically, if a type has been marked as localizable, there will be an embedded resource file associated with the Type and that file will contain a ">>$this.Name" value. Here's some sample code:
private void CheckLocalizability()
{
try
{
Assembly activeAssembly = Assembly.GetAssembly(this.GetType());
Type[] types = activeAssembly.GetTypes();
foreach (Type type in types)
{
if (TypeIsInheritedFrom(type, "UserControl") || TypeIsInheritedFrom(type, "Form"))
{
bool localizable = false;
System.IO.Stream resourceStream = activeAssembly.GetManifestResourceStream(string.Format("{0}.resources", type.FullName));
if (resourceStream != null)
{
System.Resources.ResourceReader resourceReader = new System.Resources.ResourceReader(resourceStream);
foreach (DictionaryEntry dictionaryEntry in resourceReader)
{
if (dictionaryEntry.Key.ToString().Equals(">>$this.Name", StringComparison.InvariantCultureIgnoreCase))
{
localizable = true;
break;
}
}
}
if (!localizable)
{
Debug.Assert(false, string.Format("{0} is not marked localizable.", type.FullName));
}
}
}
}
catch (Exception ex)
{
Debug.Assert(false, string.Format("Exception occurred: Unable to check localization settings. {0}", ex.Message));
}
}
private bool TypeIsInheritedFrom(Type type, string baseType)
{
while (type != null)
{
if (type.Name.Equals(baseType, StringComparison.InvariantCultureIgnoreCase))
return true;
type = type.BaseType;
}
return false;
}
Please let me know if this helps.

Related

Gong WPF: Default functionality when implementing IDropBehavior

I'm using Gong WPF to drag and drop a ListView with an ObservableCollection() as the ItemSource and the default behavior works great.
I recently added grouping to the listview and found that the default behaviors can't d&d between groups. I also need to override behavior so that the group is updated when dropping into a new group in the list.
So I implemented IDropBehavior and the two handlers and I can intercept the events as expected, but now the default d&d behavior of the automatic list move operations no longer works at all - within the same group or across groups.
Code is this:
public class RequestListModel : IDropTarget {
public void DragOver(IDropInfo dropInfo)
{
dropInfo.Effects = DragDropEffects.Move;
dropInfo.DropTargetAdorner = DropTargetAdorners.Insert;
//dropInfo.NotHandled = true;
}
public void Drop(IDropInfo dropInfo)
{
var item = dropInfo.TargetItem as HttpRequestData;
if (item == null) return;
var source = dropInfo.Data as HttpRequestData;
if (source == null) return;
if (source.Group != item.Group)
{
source.Group = item.Group;
source.RequestContent = source.ToRequestHttpHeader(true, true, true, StressTester.Options.SiteBaseUrl);
GroupRefresh();
}
//dropInfo.NotHandled = true;
}
}
Is it possible to override IDropTarget and still get the default behavior that automatically moves items, or is it now necessary to explicitly move the items in the collection manually? I was hoping the droptInfo.NotHandled switch might help but that seems to have no effect set either way...
--- updated
It's simple enough to do the collection updates manually since Gong conveniently provides the drop index. So it's easy to:
remove the source item
re-insert at the drop location
In the Drop() method above this works:
AppModel.Requests.Remove(source);
var idx = dropInfo.InsertIndex;
if (idx < AppModel.Requests.Count)
AppModel.Requests.Insert(dropInfo.InsertIndex, source);
else
AppModel.Requests.Add(source);
Easy as that is, it still seems like the default behavior (which appears to do roughly this) should still be available in some way without manually shifting items around.

How to show computed property values in EPiServer 8?

In page edit mode I want to show a read-only text that is based on a page property value. The text could for example be "A content review reminder email will be sent 2015-10-10", where the date is based on the page published date + six months (a value that will be configurable and therefore can change anytime). So far I've tried to accomplish something like this by adding another property on the page.
I've added the property CurrentReviewReminderDate to an InformationPage class we use. In page edit mode the property name is shown, but it doesn't have a value. How do I do to show the value in page edit mode (preferably as a label)?
[CultureSpecific]
[Display(
Name = "Review reminder date",
Description = "On this date a reminder will be sent to the selected mail to remember to verify page content",
Order = 110)]
[Editable(false)]
public virtual string CurrentReviewReminderDate
{
get
{
var daysUntilFirstLevelReminder =
int.Parse(WebConfigurationManager.AppSettings["PageReviewReminder_DaysUntilFirstLevelReminder"]);
if (CheckPublishedStatus(PagePublishedStatus.Published))
{
return StartPublish.AddDays(daysUntilFirstLevelReminder).ToString();
}
return "";
}
set
{
this.SetPropertyValue(p => p.CurrentReviewReminderDate, value);
}
}
EPiServer internally uses the GetPropertyValue method (i.e. the opposite of SetPropertyValue) when retrieving content for the UI.
This makes sense, otherwise your "made-up" value would be stored as the real value whenever the content is saved. This would make fall-back values etc impossible to implement.
So, this is by-design (and quite wisely so) in EPiServer. :)
However, you can customize how properties work by:
Using custom editors by applying UI hints
Modifying property metadata (for example, to display a generated value as a watermark in a textbox without interfering with the actual value being saved)
I could be misunderstanding what you're trying to do, but off the top of my head it looks like a custom editor could be a viable option for your use case?
Another solution would be to hook into the LoadedPage-event and add the value from there. This might not be the best way performance-wise since you need to do a CreateWritableClone, but depending on the site it might not matter.
[InitializableModule]
[ModuleDependency(typeof(EPiServer.Web.InitializationModule))]
public class EventInitialization : IInitializableModule
{
public void Initialize(InitializationEngine context)
{
ServiceLocator.Current.GetInstance<IContentEvents>().LoadedContent += eventRegistry_LoadedContent;
}
void eventRegistry_LoadedContent(object sender, ContentEventArgs e)
{
var p = e.Content as EventPage;
if (p != null)
{
p = p.CreateWritableClone() as EventPage;
p.EventDate = p.StartPublish.AddDays(10);
e.Content = p;
}
}
}

Pattern for binding commands on a child ViewModel from a parent menu

I'm creating a WPF MVVM app using Caliburn Micro. I have a set of buttons in a menu (Ribbon) that live in the view for my shell view model, which is a ScreenConductor. Based on the currently active Screen view model, I would like to have the ribbon buttons be disabled/enabled if they are available for use with the active Screen, and call actions or commands on the active Screen.
This seems like a common scenario. Is there a pattern for creating this behavior?
Why don't you do the reverse thing, instead of checking which commands are supported by the current active screen, let the active screen populate the menu or ribbon tab with all the controls that it supports, (i would let it inject its own user control which might just be a complete menu or a ribbon tab all by itself), this will also enhance the user experience as it will only show the user the controls that he can work with for the current active screen.
EDIT: Just looking at your question again and I'm thinking that this is much simpler than it looks
The only issue I can see you having is that a lack of a handler (and guard) method on a child VM will mean that buttons that don't have an implementation on the currently active VM will still be enabled.
The default strategy for CM is to try and find a matching method name (after parsing the action text) and if one is not found, to leave the button alone. If you were to customise that behaviour so that the default is for buttons to be disabled, you could easily get it working by just implementing the command buttons in your shell, making sure to set the command target to the active item:
In the shell define your buttons, making sure they have a target that points to the active child VM
<Button cal:Message.Attach="Command1" cal:Action.TargetWithoutContext="{Binding ActiveItem}" />
Then just implement the method in your child VM as per usual
public void Command1() { }
and optionally a CanXX guard
public bool CanCommand1
{
get
{
if(someCondition) return false;
return true;
}
}
Assuming you don't get much more complex than this, it should work for you
I'm going to have a quick look at the CM source and see if I can come up with something that works for this
EDIT:
Ok you can customise the ActionMessage.ApplyAvailabilityEffect func to get the effect you want - in your bootstrapper.Configure() method (or somewhere at startup) use:
ActionMessage.ApplyAvailabilityEffect = context =>
{
var source = context.Source;
if (ConventionManager.HasBinding(source, UIElement.IsEnabledProperty))
{
return source.IsEnabled;
}
if (context.CanExecute != null)
{
source.IsEnabled = context.CanExecute();
}
// Added these 3 lines to get the effect you want
else if (context.Target == null)
{
source.IsEnabled = false;
}
// EDIT: Bugfix - need this to ensure the button is activated if it has a target but no guard
else
{
source.IsEnabled = true;
}
return source.IsEnabled;
};
This seems to work for me - there is no target for methods which couldn't be bound to a command, so in that case I just set IsEnabled to false. This activates buttons only when a method with a matching signature is found on the active child VM - obviously give it a good test before you use it :)
Create methods and accompanying boolean properties for each of your commands on your shell view model. (See code below for an example.) Caliburn.Micro's conventions will wire them up to the buttons for you automatically. Then simply raise property changed events for the boolean properties when you change views to have them be re-evaluated.
For example, let's say you have a Save button. The name of that button in your xaml would be Save, and in your view model, you would have a Save method along with a CanSave boolean property. See below:
public void Save()
{
var viewModelWithSave = ActiveItem as ISave;
if (viewModelWithSave != null) viewModelWithSave.Save();
}
public bool CanSave { get { return ActivateItem is ISave; } }
Then, in your conductor, whenever you change your active screen, you would call NotifyOfPropertyChange(() => CanSave);. Doing this will cause your button to be disabled or enabled depending upon if the active screen is capable of dealing with that command. In this example, if the active screen doesn't implement ISave, then the Save button would be disabled.
I would use the Caliburn.Micro event aggregation in this scenario, as follows:
Create a class named ScreenCapabilities with a bunch of Boolean attributes (e.g. CanSave, CanLoad, etc.)
Create a message named ScreenActivatedMessage with a property of type ScreenCapabilities
Create a view model for your ribbon that subscribes to (handles) the ScreenActivatedMessage
In the ribbon view model's Handle method, set the local CanXXX properties based on the supplied ScreenCapabilities.
It would look something like this (code typed by hand, not tested):
public class ScreenCapabilities
{
public bool CanSave { get; set; }
// ...
}
public class ScreenActivatedMessage
{
public ScreenCapabilities ScreenCapabilities { get; set; }
// ...
}
public class RibbonViewModel : PropertyChangedBase, IHandle<ScreenActivatedMessage>
{
private bool _canSave;
public bool CanSave
{
get { return _canSave; }
set { _canSave = value; NotifyPropertyChanged(() => CanSave); }
}
// ...
public void Handle(ScreenActivatedMessage message)
{
CanSave = message.ScreenCapabilities.CanSave;
// ...
}
}
Then, somewhere appropriate, when the screen changes, publish the message. See see Caliburn.Micro wiki for more info.
Define a property (let's say ActiveScreen) for the active screen in the shell view model.
And let's assume you have properties for the each button such as DeleteButton, AddButton.
Screen is a viewmodel for the screens.
private Screen activeScreen;
public Screen ActiveScreen
{
get
{
return activeScreen;
}
set
{
activeScreen= value;
if (activeScreen.Name.equals("Screen1"))
{
this.AddButton.IsEnabled = true;
this.DeleteButton.IsEnabled = false;
}
if else (activeScreen.Name.equals("Screen2"))
{
this.AddButton.IsEnabled = true;
this.DeleteButton.IsEnabled = true;
}
NotifyPropertyChanged("ActiveScreen");
}
}

Change referenceequals to equals in reference.cs

I have a wpf app that uses a wcf webservice. Its my webservice and app, so I can make changes to either side. In the Reference.cs file that gets automatically genereated by visual studio it uses this code for the property changed event:
[System.Runtime.Serialization.DataMemberAttribute()]
public string Value {
get {
return this.ValueField;
}
set {
if ((object.ReferenceEquals(this.ValueField, value) != true)) {
this.ValueField = value;
this.RaisePropertyChanged("Value");
}
}
}
For strings though what I would really like is this:
[System.Runtime.Serialization.DataMemberAttribute()]
public string Value {
get {
return this.ValueField;
}
set {
if ((object.ReferenceEquals(this.ValueField, value) != true)) {
if (this.ValueField != value)
{
this.ValueField = value;
this.RaisePropertyChanged("Value");
}
}
}
}
That way the property changed event would not go off if the value is the same. Why this is an issue is because I listen to the OnPreviewTextInput of a textbox and change the value programmatically, then the event goes off twice, once because I changed it and once because wpf changed it via binding.
Thanks,
If you control both the server and the client, you can define your type in a seperate assembly, which you then reference from both projects.
In the WCF reference add dialog advanced settings you can tell it to re-use types, then it will use whatever implementation of your data object exists in the common assembly on the client.

Can we bind dynamic data (dataset or list) to a control in WPF

I have a user control...and the base page(s) which uses this user control has a dataset which will be used by the user control.
My dataset is dynamic...means..it can have different number of columns depending upon which page my usercontrol is implemented. Is there any control in wpf which i can use to bind this dataset (without knowing the column information) ...i mean similar to datagrid/gridview in 2.0 ?
Take a look at the WPF toolkit, this contains a Grid which meets your requirements.
This toolkit is built by Microsoft, and part of the Microsoft Shared Source Initiative (see the link I provided).
It's not supported my Microsoft though, so if you get a bug you can use their forums but not call MS support.
If you do want to do it yourself, you can for example write some code that takes a List<T>, you get the generic type, get the properties, iterate over them, write the column headers, iterate over all the items in the list and write all the properties.
I wrote this code a while back to write a List to an HTML table, I hope it's useful:
public void PrintList<T>(List<T> list)
{
if(null!=list.FirstOrDefault())
{
Type t = typeof(list[0]);
PropertyInfo[] properties = t.GetProperties();
// properties = list of all properties.
print("<table><tr>");
foreach(var property in properties)
{
// print property title
print(string.Format("<th>{0}</th>", property.Name));
}
print("</tr>");
foreach(var item in list)
{
print("<tr>");
foreach(var property in properties)
{
var propertyValue = t.InvokeMember(property.Name, BindingFlags.GetProperty, null, item, new object[] {});
print(string.Format("<td>{0}</td>", propertyValue));
}
print("</tr>");
}
print("</table>");
}
}

Resources