How to define an constructor argument accessible from Xaml - c#

Xamarin's ListView defines a 1-argument constructor as follows:
public ListView([Parameter("CachingStrategy")] ListViewCachingStrategy cachingStrategy)
As a result, CachingStrategy can be used in Xaml:
<ListView CachingStrategy="RecycleElement" .../>
I'm wondering how I can do the same thing. The following code, as is, does not compile because ParameterAttribute is internal to Xamarin.Forms:
public ItemListControl([Parameter("IsReadOnly")] bool isReadOnly)
I copied class ParameterAttribute from Xamarin.Forms, and the above compiled, but had no effect on Xaml processing. Here is the class, for reference:
[AttributeUsage(AttributeTargets.Parameter)]
internal sealed class ParameterAttribute : Attribute
{
public ParameterAttribute(string name)
{
Name = name;
}
public string Name { get; }
}
Is there anything I'm missing?

To make things simpler, I would recommend creating a BindableProperty for IsReadOnly. But you can always use x:Arguments to pass in parameters to constructor:
<local:ItemListControl ...>
<x:Arguments>
<x:Boolean>true</x:Boolean>
</x:Arguments>
</local:ItemListControl>
EDIT - 1
There is one hack that you can use - (I wouldn't recommend as this could change anytime with an update in XAMLC compilation) - but you can make sure to keep the namespace same as the one used internally while defining the parameter attribute.
namespace Xamarin.Forms
{
[AttributeUsage(AttributeTargets.Parameter)]
internal sealed class ParameterAttribute : Attribute
{
public ParameterAttribute(string name)
{
Name = name;
}
public string Name { get; }
}
}
And XAML usage would look like:
<local:ItemListControl IsReadOnly="true" .. />
EDIT - 2
This hack only seems to work if XAMLCompilation is applied to host control/page.

Related

WPF - Get text from static Dictionary Singleton

I got the localization for my project in an external class library, because I want only one Lang.csv file for my translations.
For this I got a static instance Translator.TI with an indexer in the namespace TimeTracking.Lang and my WPF Application is in the namespace TimeTracking.View in the main project (so different projects, but same basic namespace).
namespace TimeTracking.Lang
{
public class Translator
{
public static readonly Translator TI = new Translator();
private readonly Dictionary<string, Translation> _translations;
public string this[string key]
{
get { ... }
}
...
}
}
Now I want to load my translations from this static singleton instance into the view.
<Window
...
xmlns:p="clr-namespace:TimeTracking.Lang;assembly=TimeTrackingShared"
Title="{x:Static p.Translator.TI[TimeTracking]}"
>
...
</Window>
Question: Is this possible and how? And if not: Could I do this by using a class property targeting the static singleton?
It is not possible to use indexer with {x:Static} extension. You could achieve this with Binding, for example:
Title="{Binding Source={x:Static p:Translator.TI}, Path='[TimeTracking]'}"
It is also a good idea to implement INotifyPropertyChanged interface in your Translator class to avoid binding memory leaks and to enable property change notifications.

How to access App.Current properties in Windows Universal Apps

I am trying to bind to App.Current.XYZ properties in my View, however this doesn't seem to be possible, here's an example of what I have:
sealed partial class App : Application
{
public MyClassType MyClass { get; private set; }
...
And here is the View:
<Page ...
DataContext="{Binding MyClass, Source={x:Static Application.Current}}">
So, this isn't possible because x:Static is no longer supported in Windows Universal (or WinRT), so I have tried exposing the application property through a property in the code-behind, like this:
public MyClassType MyClass
{
get
{
return Application.Current.MyClass;
}
}
This doesn't work either! There is no intellisense for MyClass, it's completely missing. I have also tried App.Current and still no luck.
Any ideas why my property is not visible through Application.Current.? Or if there is any way I can bind to this property directly through XAML?
You need to cast Application.Current to your type like so:
public MyClassType MyClass
{
get
{
return ((App)Application.Current).MyClass;
}
}
Here is something that may work for you:
Create two classes:
public class MyDataProvider
{
private static readonly MyDataContainer _myDataContainer = new MyDataContainer();
public MyDataContainer MyDataContainer { get { return _myDataContainer; } }
}
public class MyDataContainer
{
public MyClassType MyClass { get; private set; }
...
}
Then in App.xaml define this static resource:
<resources:MyDataProvider x:Key="MyDataProvider"/>
Now you should be able to use data binding like this in your XAML code:
Attribute="{Binding MyDataContainer.MyClass, Source={StaticResource MyDataProvider}}"
In your case you could tweak the code so that MyDataContainer is actually your app:
public class MyDataProvider
{
public Application App { get { return Application.Current; } }
}
and write your data binding like this:
Attribute="{Binding App.MyClass, Source={StaticResource MyDataProvider}}"
In general however I would not use the App class as a provider for sources for data binding. For separation of concerns I would use something like I have above with MyDataProvider and MyDataContainer

Expose a Control of a Master Page via Interface

I have a simple interface like this:
interface IToolbarMaster {
ToolBar Toolbar { get; }
}
I added this Interface to my MasterPage and moved the field declaration of the Toolbar-Control to the Code Behind File:
public partial class Layout : ctrls.MasterPage, IToolbarMaster {
public global::Some.Web.Controls.ToolBar Toolbar;
(...)
}
My pages need to access this Toolbar Control via the Interface (we have a .FindMaster<IToolbarMaster>() Method) - but the code says, Layout doesn't implement IToolbarMaster
I could implement a property like
public ToolBar ToolBar { get { return this.Toolbar; } } // Different name spelling
But this looks really odd in my opinion.
I ran into a similar issue in VB using an auto property with a default value.
Public Property MessagePlaceHolder() As PlaceHolder = Me.phMessage
Apparently that doesn't work. I had to create the property the traditional way.
Public ReadOnly Property MessagePlaceHolder() As PlaceHolder
Get
Return Me.phMessage
End Get
End Property
Wow, I already got the answer, I was very close.
I tried this
public global::Some.Web.Controls.ToolBar Toolbar { get; }
but it didn't work. But as I added the setter everything was fine:
public global::Some.Web.Controls.ToolBar Toolbar { get; set; }
I'm leaving this for future reference, if anyone has this problem too.

Adding an Attribute for property in runtime (for design-time purposes)

I need to add an attribute to some property in runtime (for design-time purposes). I know I can do this for classes:
TypeDescriptor.AddAttributes(typeof(MyType), new MyRuntimeAttribute());
But I can't find any way to do the same for properties.
Any suggestions?
UPD: The exactly task is following:
public class BaseClass {
public BaseClass(string name) { Name = name; }
public Name { get; set; }
}
public class Descendant1: BaseClass {
public Descendant1() : base("Default Name 1") { }
}
...
public class DescendantN: BaseClass {
public DescendantN() : base("Default Name N") { }
}
I want each of the descendants to have each own DefaultValueAttribute at the Name property with the corresponding default name. And I don't want to hardcode DefaultValueAttribute on each descentand :)
You can't dynamically add or remove attributes. Note that TypeDescriptor doesn't actually add an attribute to the class either: If you check the array that typeof(MyType).GetCustomAttributes(false) returns after you attach your attribute with MyRuntimeAttribute you'll notice that it isn't part of it.
Since you mention design-time, what you can do is dynamically modify attributes. Is that what you actually want to do?
See also:
Can attributes be added dynamically in C#?
Remove C# attribute of a property dynamically
You can also provide a control designer and in that designer you can override PreFilterProperties. While typically this is used to hide properties, it can also be used to add them.
http://www.codeproject.com/KB/webforms/HidingProperties.aspx

How can a WPF UserControl inherit a WPF UserControl?

The following WPF UserControl called DataTypeWholeNumber which works.
Now I want to make a UserControl called DataTypeDateTime and DataTypeEmail, etc.
Many of the Dependency Properties will be shared by all these controls and therefore I want to put their common methods into a BaseDataType and have each of these UserControls inherit from this base type.
However, when I do that, I get the error: Partial Declaration may not have different base classes.
So how can I implement inheritance with UserControls so shared functionality is all in the base class?
using System.Windows;
using System.Windows.Controls;
namespace TestDependencyProperty827.DataTypes
{
public partial class DataTypeWholeNumber : BaseDataType
{
public DataTypeWholeNumber()
{
InitializeComponent();
DataContext = this;
//defaults
TheWidth = 200;
}
public string TheLabel
{
get
{
return (string)GetValue(TheLabelProperty);
}
set
{
SetValue(TheLabelProperty, value);
}
}
public static readonly DependencyProperty TheLabelProperty =
DependencyProperty.Register("TheLabel", typeof(string), typeof(BaseDataType),
new FrameworkPropertyMetadata());
public string TheContent
{
get
{
return (string)GetValue(TheContentProperty);
}
set
{
SetValue(TheContentProperty, value);
}
}
public static readonly DependencyProperty TheContentProperty =
DependencyProperty.Register("TheContent", typeof(string), typeof(BaseDataType),
new FrameworkPropertyMetadata());
public int TheWidth
{
get
{
return (int)GetValue(TheWidthProperty);
}
set
{
SetValue(TheWidthProperty, value);
}
}
public static readonly DependencyProperty TheWidthProperty =
DependencyProperty.Register("TheWidth", typeof(int), typeof(DataTypeWholeNumber),
new FrameworkPropertyMetadata());
}
}
Ensure that you have changed the first tag in the xaml to also inherit from your new basetype
So
<UserControl x:Class="TestDependencyProperty827.DataTypes.DataTypeWholeNumber"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="clr-namespace:System;assembly=mscorlib"
>
becomes
<myTypes:BaseDataType x:Class="TestDependencyProperty827.DataTypes.DataTypeWholeNumber"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="clr-namespace:System;assembly=mscorlib"
xmlns:myTypes="clr-namespace:TestDependencyProperty827.DataTypes"
>
So, to summarise the complete answer including the extra details from the comments below:
The base class should not include a xaml file. Define it in a single (non-partial) cs file and define it to inherit directly from Usercontrol.
Ensure that the subclass inherits from the base class both in the cs code-behind file and in the first tag of the xaml (as shown above).
public partial class MooringConfigurator : MooringLineConfigurator
{
public MooringConfigurator()
{
InitializeComponent();
}
}
<dst:MooringLineConfigurator x:Class="Wave.Dashboards.Instruments.ConfiguratorViews.DST.MooringConfigurator"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:dst="clr-namespace:Wave.Dashboards.Instruments.ConfiguratorViews.DST"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot" Background="White">
</Grid>
</dst:MooringLineConfigurator>
I found the answer in this article: http://www.paulstovell.com/xmlnsdefinition
Basically what is says is that you should define an XML namespace in the AssemlyInfo.cs file, which can the be used in the XAML. It worked for me, however I placed the base user control class in a separate DLL...
There is partial class definition created by designer, you can open it easy way via InitializeComponent() method definition.
Then just change partial class iheritence from UserControl to BaseDataType (or any you specified in class definition).
After that you will have warning that InitializeComponent() method is hidden in child class.
Therefore you can make a CustomControl as base clas instead of UserControl to avoid partial definition in base class (as described in one comment).
I ran into the same issue but needed to have the control inherit from an abstract class, which is not supported by the designer. What solved my problem is making the usercontrol inherit from both a standard class (that inherits UserControl) and an interface. This way the designer is working.
//the xaml
<local:EcranFiche x:Class="VLEva.SIFEval.Ecrans.UC_BatimentAgricole"
xmlns:local="clr-namespace:VLEva.SIFEval.Ecrans"
...>
...
</local:EcranFiche>
// the usercontrol code behind
public partial class UC_BatimentAgricole : EcranFiche, IEcranFiche
{
...
}
// the interface
public interface IEcranFiche
{
...
}
// base class containing common implemented methods
public class EcranFiche : UserControl
{
... (ex: common interface implementation)
}

Categories