XAML c# instantiating object overloaded constructor [duplicate] - c#

While using WPF I noticed that when I add a control to a XAML file, the default constructor is called.
Is there a way to call a parameterized constructor?

.NET 4.0 brings a new feature that challenges the answer - but apparently only for UWP applications (not WPF).
x:Arguments Directive
<object ...>
<x:Arguments>
oneOrMoreObjectElements
</x:Arguments>
</object>

One of the guiding principles of XAML-friendly objects is that they should be completely usable with a default constructor, i.e., there is no behavior that is only accessible when using a non-default constructor. To fit with the declarative nature of XAML, object parameters are specified via property setters. There is also a convention that says that the order in which properties are set in XAML should not be important.
You may, however, have some special considerations that are important to your implementation but at odds with convention:
You may have one or more properties which must be set before the object can be used.
Two or more properties may be mutually exclusive with each other, e.g., it makes no sense to set both the StreamSource and UriSource of an image.
You may want to ensure that a property is only set during initialization.
One property may depend on another, which can be tricky due to the aforementioned convention of order independence when setting properties.
To make it easier to handle these cases, the ISupportInitialize interface is provided. When an object is read and created from XAML (i.e., parsed), objects implementing ISupportInitialize will be handled specially:
The default constructor will be called.
BeginInit() will be called.
Properties will be set in the order they appeared in the XAML declaration.
EndInit() is called.
By tracking calls to BeginInit() and EndInit(), you can handle whatever rules you need to impose, including the requirement that certain properties be set. This is how you should handle creation parameters; not by requiring constructor arguments.
Note that ISupportInitializeNotification is also provided, which extends the above interface by adding an IsInitialized property and Initialized event. I recommend using the extended version.

No. Not from XAML [when using WPF].

Yes, you can do it by the ObjectDataProvider. It allows you to call non-default constructor, for example:
<Grid>
<Grid.Resources>
<ObjectDataProvider x:Key="myDataSource"
ObjectType="{x:Type local:Person}">
<ObjectDataProvider.ConstructorParameters>
<system:String>Joe</system:String>
</ObjectDataProvider.ConstructorParameters>
</ObjectDataProvider>
</Grid.Resources>
<Label Content="{Binding Source={StaticResource myDataSource}, Path=Name}"></Label>
</Grid>
assuming that Person is
public class Person
{
public Person(string Name)
{
this.Name = Name;
}
public string Name { get; set; }
}
Unfortunately, you cannot bind the ConstructorParameters. See some workaround here.

Related

Declaring Design-Time ViewModel for Avalonia Window

I'm looking for the right approach to declare design-time ViewModel for an Avalonia window.
Some samples suggest
d:DataContext="{d:DesignInstance viewModels:LoginViewModel, IsDesignTimeCreatable=True}"
This throws
XamlParseException at 5:5: Unable to resolve type DesignInstance from namespace http://schemas.microsoft.com/expression/blend/2008
Default Avalonia MVVM template suggests
<Design.DataContext>
<vm:MainWindowViewModel/>
</Design.DataContext>
If the ViewModel takes parameters, it throws
XamlLoadException at 16:10: Unable to find public constructor for type Demo.CloseNonModalDialog:Demo.CloseNonModalDialog.CurrentTimeDialogViewModel()
I guess adding a default parameter-less constructor is an option.
With MvvmLight/WPF, I used to reference the ViewLocator as a static resource
DataContext="{Binding Source={StaticResource Locator}, Path=MainWindow}"
That's an option, although I haven't yet found the right way to declare and reference the resource.
What is the recommended approach here? If I want to show design-time data, I'd say only the 3rd option would work. Which is not the option shown in samples.
Unable to find public constructor for type Demo.CloseNonModalDialog:Demo.CloseNonModalDialog.CurrentTimeDialogViewModel()
You can specify arguments via x:Arguments XAML directive, see https://learn.microsoft.com/en-us/dotnet/desktop/xaml-services/xarguments-directive
That's an option, although I haven't yet found the right way to declare and reference the resource.
I'd suggest to declare DesignData class and use x:Static, it will give you way more flexibility. e. g.
class DesignData
{
public MyViewModel MyViewModel => new MyViewModel(...);
}
d:DataContext="{x:Static local:DesignData.MyViewModel}"
View model creation would also not happen during the normal app execution unlike the StaticResource approach.

C# resource file - how to access internal resources from XAML?

This is my simple (newbie) understanding:
We have a choice for the Ressource Code Generator: internal or public. The default appears to be internal. I understand the resources are created with an internal or public accessor depending on that choice.
Therefore, if the accessor is public I can use something like:
Text="{x:Static resx:Resources.SomeLabelID}"
... in my XAML.
My question is either/both:
What use is a generated internal accessor when the XAML shown above does not work (more precisely it 'works' in design mode but not at run time thus creating confusion for a newbie)?
Note: There are many answers in SO that simply say to change the accessor to 'public' without explaining why, which I think would by nice to understand, especially since the default appears to be internal.
-OR-
Is there another way to access resource strings (from XAML) when the code is generated with the internal accessor ?
Thank you
Everything defined in XAML is using internal access modifier. That is by design of WPF framework, simply so you will not be able to access locally defined controls in different assembly.
Generally if you are going to use strings for "static" controls (i.e. Label) then you can simply go a head with changing access modifier.

Understanding XAML syntax

I have a Theme attribute that I can load like this:
<xcad:DockingManager>
<xcad:DockingManager.Theme>
<xcad:AeroTheme>
</xcad:AeroTheme>
</xcad:DockingManager.Theme>
AeroTheme is a class. How can I achieve the same result via attributes?
<xcad:DockingManager Theme="What should I write here?">
You need an instance of that theme, which you can provide as a static resource. For example if the parent control is a grid:
<Grid.Resources>
<xcad:AeroTheme x:Key="myTheme"/>
</Grid.Resources>
<xcad:DockingManager Theme="{StaticResource myTheme}">
Generally, it's impossible to set a property of a complex type (a class for example) via attribute in xaml without declaring this object in Xaml explicitelly. When XAML is parsed the actual object tree is being constructed. Xaml give you the possibility to use attributes in much more extended way than common XML. In particular, you can write something like this:
<Grid Backgroud="Red"/>
In compile time, Red is nothing more than string value. However WPF does the magic behind the scene. The magic is that WPF read the type of property via reflection (in this example Background property type is a Brush), it finds TypeConverterAttribute on Brush class (once again via reflection) and use appropriate classes derieving from TypeConverter to make a conversion from string. In this example WPF would use BrushConverter class (located in PresentationCore assmebly) and call ConvertFrom method to get actual Brush object to set the Background property.
Of course using this way is not straightforward. In your example, to make it possible you should have the following conditions satisfied:
A class AeroTheme should be marked with TypeConverterAttribute
The type specified in the attribute is an instance of TypeConverter class.
In your TypeConverter you have the logic for converting from string to AeroTheme object (via implementing method from TypeConverter).
Code example:
[TypeConverter(typeof(AeroThemeConverter)]
public class AeroTheme
{
...
}
public class AeroThemeConverter : TypeConverter
{
//implementation of convertion here
}
In this way if you would have in XAML:
<xcad:DockingManager Theme="Aero">
WPF does the magic and set your Theme property using the conversion written in AeroThemeConverter.
This is of course neighter full nor work example, but it shows the idea. To get more information about this see the MSDN documentation:
https://msdn.microsoft.com/en-us/library/system.componentmodel.typeconverter(v=vs.110).aspx
So the only way to set a property via attribute (without declaring it in XAML) is to make use of the described mechanism.
Another way is to declare an object and use a MarkupExtension (StaticResource, DynamicResource, various types of Binding etc) as shown above.

Having my Viewmodel appear in namespace dropdown

I'm trying to expose the ViewModel as a static resource on the page so that it can be easily accessible by the binding.
TestViewModel.cs
namespace Test.WPFUI.Home
{
public class TestViewModel....
HelloWorldView.Xaml
xmlns:local="clr-namespace:Test.WPFUI.Home"
<UserControl.Resources>
<local:TestViewModel x:Key="mainPageViewModel" />
</UserControl.Resources>
TestViewModel Can't be found. May I ask for some tips or suggestions Please.
Getting help from http://www.telerik.com/help/silverlight/gridview-troubleshooting-blank-cells.html
public class LoanViewModel : ScreenViewModelBase<LoanViewModel>, IRecord, INotifyPropertyChanged
{
public LoanViewModel(IEventAggregator events) .............
It sounds like your initial problem was not having the full xmlns definition. You usually need both the namespace and assembly.
The easiest way to get it right, in my experience, is to let intellisense do it for you. Just start typing the namespace you want, and as long as its in a referenced project, there will be an autocomplete option.
Your second problem is due to not having a default constructor. You wrote this:
<local:TestViewModel x:Key="mainPageViewModel" />
Which will invoke the default constructor. However, you define a constructor here:
public LoanViewModel(IEventAggregator events) .............
Which removes the provided (paramaterless) default constructor. I'm going to take a wild guess and say that creating the correct IEventAggregator is not simple or desired from XAML, so I see two choices:
You didn't really need that parameter in the constructor. Simply add a default constructor to your view model and you are good to go!
You really need that parameter, so instantiating from XAML just isn't a good idea. Pass in your view model from somewhere else on the view's constructor.
If you feel like you can instantiate the correct object from XAML, use this post to invoke the paramaterized constructor: Calling a parameterized constructor from XAML
In my opinion, putting truly regular classes into XAML is not a good pattern to follow, so I wouldn't. By regular, I mean not related at all to the view.

Using MEF in controls instantiated from XAML

I have a UserControl I've created which imports several parts using the [Import] attribute.
public class MyUserControl : UserControl, IPartImportsSatisfiedNotification
{
[Import]
public IService Service { get; set; }
public MyUserControl()
{
}
public void OnImportsSatisfied()
{
// Do something with Service.
}
}
This UserControl is instantiated from XAML, so its imports aren't being satisfied and OnImportsSatisfied isn't being called.
<local:MyUserControl />
My question is how can I satisfy my class's imports when it's being created in XAML.
From MSDN:
To be instantiated as an object element in XAML, a custom class must
meet the following requirements:
The custom class must be public and must expose a default (parameterless) public constructor. (See following section for notes
regarding structures.)
The custom class must not be a nested class. The extra "dot" in the full-name path makes the class-namespace division ambiguous, and
interferes with other XAML features such as attached properties.
If an object can be instantiated as an object element, the created object
can fill the property element form of any properties that take the
object as their underlying type.
You can still provide object values
for types that do not meet these criteria, if you enable a value
converter. For more information, see Type Converters and Markup
Extensions for XAML.
From there, you have two choices:
1) Using a TypeConverter:
Using a type converter will allow you to instantiate an object without a parameterless constructor, but you will have to provide a TypeConverter that will do the instantiation.
Now, I never had to use it, I cannot help you further with that.
2) Retrieve IService using the ServiceLocator:
public class MyUserControl : UserControl
{
public IService Service { get; set; }
public MyUserControl()
{
Service = Microsoft.Practices.ServiceLocation.ServiceLocator.Current.GetInstance<IService>();
// You can do something with Service here already.
}
}
I realize it is a change in the design of your class, but hopefully you can cope with it.
Hope this helps,
Bab.
if you did not want mef to create your usercontrol, you have to use the compositioncontainer in your usercontrol and call GetExport direct. but then you have the problem to get the instance of your compositioncontainer :)
ps: i let mef create my wpf views in my applications.
(I'm resurrecting this in case anyone comes across it. As a disclaimer, I'm no expert and these are just solutions I found to be working.)
I found that calling CompositionContainer.ComposeParts(myUserControl) works. I call this on the control's constructor. You'll need to get a reference to the CompositionContainer somehow:
public MyUserControl()
{
compositionContainer.ComposeParts(this);
}
Additional solution:
This is probably unnecessary, but here's another way. This is far more convoluted but it does allow you to "Import" your usercontrol in XAML.
To have your imports satisfied, MyUserControl needs to be exported and then instantiated by MEF. My solution was to have static field in a class that holds a "Locator" object. This Locator object is responsible for importing and returning exported objects. Then I could refer to this static field in XAML, like so:
<ContentControl Content="{Binding MyUserControl, Source={x:Static v:SomeClass.Locator}}">
SomeClass has a static property called Locator which gets assigned early in the application's life cycle. The Locator could then have a MyUserControl property that gets Imported.
(Disclaimer: the links below are to my own framework and the solution, being as crude as it is, if used should be used with care.)
To provide an example of the above, I'll explain how I implemented it in my framework:
In my case, SomeClass is a subclass of System.Windows.Application that replaces App.xaml, and ViewLocator is assigned on its OnStartup, as can be seen here.
The ViewLocator class is a System.Dynamic.DynamicObject that imports views, which have a custom ViewExport attribute. Views are identified using the ViewExportAttribute.Alias property.
This is an example of a view being exported and being assigned an alias.
Finally, the MEF instantiated instance of the view can be used in XAML as follows:
<ContentControl Content="{Binding HomeView, Source={x:Static v:FrameworkApp.ViewLocator}}">

Categories