How to display application title in XAML text field - silverlight

I have a Windows Phone page code that is shared by multiple applications.
At the top of the page, I show the title of the application, like so:
Is it possible to make the text be bound to the application title as defined in the application's assembly?
I realise that I could do this by code by reading the title in the assembly and then doing something like:
this.ApplicationTitle.Text = title;
I was hoping that the title as defined in the assembly could be accessed with some magic like:
Text={assembly title}" directly from within the xaml.
Thanks

Create a property called ApplicationTitle that returns that name of the application like the following, and then bind to it in XAML:
public string ApplicationTitle
{
get { return System.Reflection.Assembly.GetExecutingAssembly().GetName().Name; }
}
(You can use a relative binding source if you can't or don't want to use the data context.)
edit:
I just realized that my method involved security considerations since GetName is a method that is [Security Critical]. And I got a MethodAccessException stating: Attempt to access the method failed: System.Reflection.Assembly.GetName()
So here's another way to get the assembly name and return it in a property by using the AssemblyTitle attribute.
public string ApplicationTitle
{
get
{
System.Reflection.AssemblyTitleAttribute ata =
System.Reflection.Assembly.GetExecutingAssembly().GetCustomAttributes(
typeof(System.Reflection.AssemblyTitleAttribute), false)[0] as System.Reflection.AssemblyTitleAttribute;
return ata.Title;
}
}
To bind in XAML, you can use this:
Text="{Binding ElementName=LayoutRoot, Path=Parent.ApplicationTitle}"

Related

Why Can't I Use My DbContext Type?

I'd like to access some static properties of my DbContext type in a WPF Window. I thought I could use the same XAML that I use to refer to individual entities:
<Window.Resources>
<entity:Account x:Key="account"/> //Works fine
<entity:MyEntities x:Key="myEntities"/> //Throws an error!
</Window.Resources>
I get this error:
No connection string named 'MyEntities' could be found in the application config file.
Why is it treating the DbContext type (MyEntities) differently than the Account entity? Is there an easy way I can access the static properties of my MyEntities type?
The syntax you used is for creating instances, not static properties. If you want to access a static property you need to use the x:Static markup extension
<Window.Resources>
<entity:Account x:Key="account" SomeProperty={x:Static entity:MyEntities.MyProperty}/>
</Window.Resources>
The above xaml would be similar to the C# code
var account = new Account()
{
SomeProperty = MyEntities.MyProperty
};
this.Resources["account"] = account;
See that you are calling new Account(), if you called new MyEntites() (like your original example did) you get the error you where getting.
It appears that particular error results due to the static constructor that I placed in my DbContext. When I remove the static constructor the error changes to:
Object reference not set to an instance of an object.
As it turns out, the original error doesn't prevent me from compiling or running my application. I changed my code to use Scott Chamberlain's suggestion (which produces a similar ignorable error) because it is much cleaner and I can access the static properties on the DbContext just fine in spite of Visual Studio's complaints. Thanks, everyone, for the help and suggestions.

A method set as ICollectionView.Filter sees other properties in the class as null even though they are not null

I'm trying to implement a basic filtered list box in WPF. The user types something and the list is narrowed to the values beginning with the typed phrase.
I have:
a View with:
a TextBox whose Text property is bound to InstitutionFilteringString property in the ViewModel class, which is set as the data context,
a ListBox whose ItemSource property is bound to an ICollectionView named Institutions in the View Model
a ViewModel class with the properties mentioned above.
Code (with irrelevant parts cut out):
class ChooseInstitiutionAndPublisherPageViewModel : WizardPageViewModelBase
{
private ICollectionView _institutions;
public ICollectionView Institutions
{
get
{
return _institutions;
}
set
{
_institutions = value;
NotifyPropertyChanged("Institutions");
}
}
private string _institutionFilteringString;
public string InstitutionFilteringString
{
get
{
return _institutionFilteringString;
}
set
{
_institutionFilteringString = value;
NotifyPropertyChanged("InstitutionFilteringString");
//WORKAROUND
//Institutions.Filter = new Predicate<object>(FilterInstitutions);
Institutions.Refresh();
}
}
public ChooseInstitiutionAndPublisherPageViewModel(WizardViewModel parent)
: base(parent)
{
Institutions = CollectionViewSource.GetDefaultView(CentralRepository.Instance.GetInstitutions());
Institutions.Filter = new Predicate<object>(FilterInstitutions);
}
private bool FilterInstitutions(object obj)
{
//I may refer directly to the field or through the property, it doesn't change anything
if (_institutionFilteringString == null || _institutionFilteringString.Length == 0)
return true;
//some more filtering, irrelevant
//[cut]
}
}
The view and the binding:
<TextBox Text="{Binding Path=InstitutionFilteringString, Mode=TwoWay}" Height="23" Margin="6,6,87,0" Name="institutionNameTextBox" VerticalAlignment="Top" TextChanged="institutionNameTextBox_TextChanged" />
<ListBox Margin="6,35" Name="institutionsListBox" ItemsSource="{Binding Path=Institutions}" IsSynchronizedWithCurrentItem="True" />
So, to the point. The setter for the InstitutionFilteringString is called correctly. Following an advice from here, the setter calls a Refresh() method on the collection view. The FilterInstitutions() method is called.
And now the bug: even though the string was set just before a second, inside the FilterInstitutions method it's null. If I go with the debugger down the call stack, from the point of view of the setter it's still set to the typed value, but inside the filtering method it's null.
In the setter there is a commented-out line of code. Uncommenting it fixes the bug, but it's hardly how it should be done.
What am I doing wrong?
(I'm not sure, but it seems to me as if the setter and the filtering method operated on two different instances of the class. But how is it possible, I create just one instance and the class is not clonable)
EDIT
I'm sorry, it seems I've lied. I've put a breakpoint in the constructor and it seems I indeed create two instances of the class and CollectionViewSource.GetDefaultView returns the same instance of ICollectionView for both. Well, but I want actually to have two views for the same collection. Well, I've followed this answer and it seems to work :)
do you create your Institutions once? and set the
Institutions.Filter = new Predicate<object>(FilterInstitutions)
once? if yes its ok :) can you post your code for this and also the code for FilterInstitutions methode? i do it all the way in my projects and have no problems.

Binding error in an activity designer using ActivityFunc<>

I'm having a hard time getting a custom activity designer of mine to display in the workflow designer. My activity includes an activity func, and I've already found a number of blogs posts dealing with them here, here, here and here.
The custom activity has an ActivityFunc<> as an input argument, and I need to expose the func in a designer as drop zone in which the user can place an "inner" activity (à la TransactionScope).
The custom activity is authored in XAML, and the func's declaration looks like this:
<x:Property Name="CompletionTest" Type="ActivityFunc(sdscmt:DmeTask, sdsav:WfPatient, sdscmc:DmeClinicalElement, x:Boolean)" />
The XAML also contains an InvokeFunc<> activity matching the CompletionTest property.
The activity designer follows the recommendations outlined in the blog posts mentionned above. In particular, it overrides OnModelItemChanged to initialize the CompletionTest property:
if (this.ModelItem.Properties["CompletionTest"].Value == null)
{
this.ModelItem.Properties["CompletionTest"].SetValue(
new ActivityFunc<DmeTask, WfPatient, DmeClinicalElement, bool>()
{
Argument1 = new DelegateInArgument<DmeTask>
{
Name = "task"
},
Argument2 = new DelegateInArgument<WfPatient>
{
Name = "patient"
},
Argument3 = new DelegateInArgument<DmeClinicalElement>
{
Name = "element"
},
Result = new DelegateOutArgument<bool>
{
Name = "success"
},
});
}
The designer's XAML looks like this:
<sap:ActivityDesigner x:Class="SoftInfo.Dme.ServicesDme.Workflow.Design.PerformTaskDesigner" ... >
<StackPanel>
<sap:WorkflowItemPresenter AllowedItemType="{x:Type sa:Activity}" Background="Transparent" MinWidth="150" MinHeight="100" HintText="Drop the completion test here" Margin="5,5,5,5" Item="{Binding Path=ModelItem.CompletionTest.Handler, Mode=TwoWay}" />
</StackPanel>
</sap:ActivityDesigner>
After all this, whenever I place an instance of my custom activity into a workflow, I get a red box labelled "Could not generate view for PerformTask" where my designer should appear. The box's tooltip indicates that an exception occurred from within the designer :
System.Windows.Markup.XamlParseException: A 'Binding' cannot be set on the 'Item' property of type 'WorkflowItemPresenter'. A 'Binding' can only be set on a DependencyProperty of a DependencyObject.
I don't understand what I'm doing wrong. I've used WorkflowItemPresenter many times before, and this is the first time I've gotten this binding error.
That's kind of a messed up way to go about this. The error may be incorrect; the inner exception would tell the tale I'd wager (sorry, listening to Game of Thrones while I work today).
I implement IActivityTemplateFactory to configure my activity delegates:
public sealed class MyActivity: NativeActivity, IActivityTemplateFactory
{
public const string ChildArgumentName = "theArgument";
public ActivityFunc<object, bool> Child { get; set; }
Activity IActivityTemplateFactory.Create(System.Windows.DependencyObject target)
{
return new MyActivity()
{
Child = new ActivityFunc<Capture, bool>
{
Argument = new DelegateInArgument<object>(ChildArgumentName )
}
};
}
}
Notice a couple things here. Yes, the design surface instantiates an instance of your Activity only to use it to create another instance of the same Activity (configured properly, of course), but this method is bulletproof. You can, if requirements dictate, move your implementation of IATF elsewhere, but if it doesn't matter who cares?
Notice that I declare in my Activity's design what the name of the Func's argument will be. You can do this in a more generic fashion (custom attributes, etc), but if your Activities fit together in a predictable fashion, I have found this is the simplest manner to automatically wire up child Activities with their parents. YMMV.
In the designer, its similar:
<sap:WorkflowItemPresenter
HintText="Add child here"
Item="{Binding ModelItem.Child.Handler}" />
that's all you need. If the signature of the Activity doesn't match, it won't fit. The child also takes advantage of IATF to bind itself to its parent:
public sealed class ChildActivity : NativeActivity<bool>, IActivityTemplateFactory
{
public InArgument<object> Target { get; set; }
Activity IActivityTemplateFactory.Create(System.Windows.DependencyObject target)
{
return new ChildActivity
{
Target = new VisualBasicValue<object>(MyActivity.ChildArgumentName)
};
}
}
If you expect your child Activity to be dropped on different targets, you might have to do some annoying inspection of the target DependencyObject, which will allow you to inspect the current workflow tree. (Please note, I'm not 100% familiar with the implicit conversion behavior of VisualBasicValue, so you might have some compiler errors with the above code!)
Another option if you have to be extra tricky (drop an Activity from the toolbox, then drag it somewhere else where you will have to re-do your inspection) is to inspect the current state of the workflow from within CacheMetadata. I haven't done this, but I believe you can and update your registrations with the design surface to reflect the current state of the workflow.

How to use Windows' Customized Region and Language settings in WPF

I am working in Namibia. Namibia is not an option on the Windows Region and Language settings, but share most cultural specifics with South Africa, so we select English South Africa and then customize the currency symbol to be "N$" (Namibian Dollars) and not "R" (South African Rand).
However, I can't convince WPF to use the customized currency. Using string.format("{0:c}", foo) in code works fine, but using {Binding Path=SomeCurrencyValue, StringFormat=c}` in XAML still uses the "R" symbol and not the custom "N$" symbol.
In App.xaml.cs I set the Application Culture with the following code:
System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo(System.Globalization.CultureInfo.CurrentCulture.LCID, true);
FrameworkElement.LanguageProperty.OverrideMetadata(typeof(FrameworkElement),
new FrameworkPropertyMetadata(
System.Windows.Markup.XmlLanguage.GetLanguage(
System.Globalization.CultureInfo.CurrentUICulture.IetfLanguageTag)));
As demonstration, here is some XAML code that shows the problem:
<StackPanel>
<TextBlock>
Formatted in XAML with: <LineBreak/>
Text="{Binding Path=SomeCurrencyValue, StringFormat=c}" <LineBreak/>
Result:
</TextBlock>
<TextBox Text="{Binding Path=SomeCurrencyValue, StringFormat=c, Mode=OneWay}"
Margin="5"/>
<TextBlock>
Formatted in code with: <LineBreak/>
return string.Format("{0:c}", SomeCurrencyValue); <LineBreak/>
Result:
</TextBlock>
<TextBox Text="{Binding Path=SomeCurrencyString, Mode=OneWay}"
Margin="5"/>
</StackPanel>
The DataContext for the above View contains the following:
public double SomeCurrencyValue
{
get { return 34.95; }
}
public string SomeCurrencyString
{
get
{
return string.Format("{0:c}", SomeCurrencyValue);
}
}
And the result looks like this:
I know there is a similiar question here, but I was hoping to get better answers with a more complete question. I am mostly working on financial applications for Namibian clients, so this is quite a serious issue for me - if there is no way to do this with .NET 4.0, I would consider filing a bug report, but I just wanted to check here first.
EDIT:
Just opened up the bounty on this question. I'm hoping for either a solution that isn't a rediculous workaround, or confirmation that this is a bug and should be filed as such.
One solution to this is to create this Namibian CultureInfo, so its fully recognized by all .NET layers. Here is a code that does it:
public static void RegisterNamibianCulture()
{
// reference the sysglobl.dll assembly for this
CultureAndRegionInfoBuilder namibianCulture = new CultureAndRegionInfoBuilder("en-NA", CultureAndRegionModifiers.None);
// inherit from an existing culture
namibianCulture.LoadDataFromCultureInfo(new CultureInfo("en-za"));
namibianCulture.CultureEnglishName = "Namibia";
namibianCulture.RegionEnglishName = "Namibia";
namibianCulture.CultureNativeName = "Namibia"; // you may want to change this
namibianCulture.RegionNativeName = "Namibia"; // you may want to change this
// see http://en.wikipedia.org/wiki/ISO_3166-1, use user-defined codes
namibianCulture.ThreeLetterISORegionName = "xna"; // I use x as 'extended' and 'na' as namibia
namibianCulture.TwoLetterISORegionName = "xn";
namibianCulture.ThreeLetterWindowsRegionName = namibianCulture.ThreeLetterISORegionName;
// see http://www.currency-iso.org/dl_iso_table_a1.xml
namibianCulture.ISOCurrencySymbol = "nad";
namibianCulture.CurrencyEnglishName = "Namibia Dollar";
namibianCulture.CurrencyNativeName = "Namibia Dollar"; // you may want to change this
// this is were you build something specific, like this symbol you need
namibianCulture.NumberFormat.CurrencySymbol = "N$";
// you'll need admin rights for this
namibianCulture.Register();
}
public static void UnregisterNamibianCulture()
{
CultureAndRegionInfoBuilder.Unregister("en-NA");
}
Once you have called the Register function once on a given machine (you will need to install this culture on end user machines), you can now use your initial WPF startup code, just change it like this:
System.Threading.Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-NA");
And everything should work as expected. You can also use standard language tags and all that jazz, since en-NA is now recognized.
I had the same problem and I started from Simon's answer (thank you, Simon) making some modifications in order to get the user configuration (Region and Language in Control Panel) every time the application is launched, not the culture default.
My code look like this one
public MainWindow()
{
CreateAndRegisterLocalizedCulture();
this.Language = XmlLanguage.GetLanguage(customCultureName);
InitializeComponent();
DataContext = new ViewModel();
}
private void CreateAndRegisterLocalizedCulture()
{
CultureAndRegionInfoBuilder customCulture = new CultureAndRegionInfoBuilder(customCultureName, CultureAndRegionModifiers.None);
// Inherits from the current culture and region, may be configured by the user
customCulture.LoadDataFromCultureInfo(CultureInfo.CurrentCulture);
customCulture.LoadDataFromRegionInfo(RegionInfo.CurrentRegion);
// Not needed for the culture sake but...
customCulture.CultureEnglishName = CultureInfo.CurrentCulture.EnglishName + "-Customized";
customCulture.CultureNativeName = CultureInfo.CurrentCulture.NativeName + "-Customized";
// If the custom culture is already registered an unregistration is needed
// otherwise the following Register() call will generate an exception
if (CultureInfo.GetCultures(CultureTypes.UserCustomCulture).Where(ci => (ci.Name == customCulture)).Count() != 0)
{
// Admin rights are needed here
CultureAndRegionInfoBuilder.Unregister(customCulture);
}
// Admin rights are needed here
customCulture.Register();
}
It works fine to me but there are two problems in this approach:
In windows 7+ you need to start the application with Administrator rights as it will create a new culture file in C:\Windows\Globalization with the name you give to your custom culture
The Unregister method does not delete the above create file but it renames it as xyz.tmp0 and, with a logic I didn't get, time to time it creates more tmp file copies (xyz.tmp1, xyz.tmp2, ...). At least it is how it works in my pc.
Something that is not really a problem but a bit weird is that, once I change my regional settings in the control panel, I have to start my application twice before I see the modification. I can survive :)

Silverlight InlineCollection.Add(InlineUIContainer) missing?

I'm having difficulty adding the inline of specific type InlineUIContainer into the InlineCollection (Content property) of a TextBlock. It appears the .Add() method of InlineCollection doesn't accept this type, however you can clearly set it through XAML without explicitly marking the content as a InlineContainer, as demonstrated in many examples:
http://msdn.microsoft.com/en-us/library/system.windows.documents.inlineuicontainer.aspx
Is it possible to programatically add one of these as in the following?
Target.Inlines.Add(new Run() { Text = "Test" });
Target.Inlines.Add(new InlineUIContainer() {
Child = new Image() { Source = new BitmapImage(new Uri("http://example.com/someimage.jpg")) } });
Target.Inlines.Add(new Run() { Text = "TestEnd" });
I have a feeling what's going on is that Silverlight is using a value converter to create the runs when specified in XAML as in the example which doesn't use InlineContainer, but I'm not sure where to look to find out.
The specific error I'm getting is as follows:
Cannot add value of type 'System.Windows.Documents.InlineUIContainer' to a 'InlineCollection' in a 'System.Windows.Controls.TextBlock'.
As pointed out by Jedidja, we need to use RichTextBox to do this in Silverlight.
You can't Add() Runs directly, but you can add Spans containing Runs.
Interestingly, you can also do this:
textBlock.Inlines.Clear();
textBlock.Inlines.Add(new Span());
textBlock.Inlines[0] = new Run();
Not that it's a good idea to hack around what the framework is actively trying to prevent you from doing.
P.S. If you can't figure out what XAML is doing, inspect the visual tree.

Resources