I'd like to create an attached property in my C++/Cx WinRT project. When I do so, the XAML compiler complains that I'm using an unknown property.
I'm declaring my attached property in a cpp file as such:
/*--------------------------------------------------------------------
Forward Declarations
--------------------------------------------------------------------*/
namespace
{
void
StateChanged(
DependencyObject^ target,
DependencyPropertyChangedEventArgs^ args);
}
/*--------------------------------------------------------------------
DependancyProperty setup for the view model
--------------------------------------------------------------------*/
namespace
{
DependencyProperty^ _stateProperty =
DependencyProperty::RegisterAttached(
"State",
TypeName(Platform::String::typeid),
TypeName(VisualStateManagerBindingHelper::typeid),
ref new PropertyMetadata(nullptr, ref new PropertyChangedCallback(&StateChanged)));
}
namespace
{
void
StateChanged(
DependencyObject^ target,
DependencyPropertyChangedEventArgs^ args)
{
if (args->NewValue != nullptr)
{
VisualStateManager::GoToState(safe_cast<Control^>(target), safe_cast<String^>(args->NewValue), true);
}
}
}
In my XAML file, I'm attempting to use the attached property as such:
<UserControl
xmlns:local="using:MyCustomNamespace"
...
<Grid Grid.Column="1" local:VisualStateManagerBindingHelper.State="{Binding Path=SomeViewModelProperty, Mode=TwoWay}">
...
When I compile, I'm faced with this error:
XamlCompiler error WMC0010: Unknown attachable member 'VisualStateManagerBindingHelper.State' on element 'Grid'
It appears to me as if the XAML compiler is supposed to get a hint somewhere that this attached property is allowed, but I don't know how to give it that hint. This article on the same subject suggests that the DependancyProperty should be public. I'm not entirely sure how to do that in C++.
My ultimate goal is to bind a visual state group's state to some view model property. The method to do this is outlined in the accepted answer in this question. Unfortunately, the code provided there is C# and I can't seem to translate it over to C++ properly.
Finally, I noticed the SuspensionManager that comes with Visual Studio's new project templates defines an attached property almost exactly like I do, but they don't show any examples of it in use. This makes me feel like I'm on track, but missing some small piece of the puzzle.
I found a site that answered my question! As I suspected, you need to have your class defined in a very specific way for the XAML preprocessor to know about your attached property. All of the requirements along with a code sample are available here.
Related
in a prism 6 based application,
this is a part of my shell, and QuickAccessToolBar region defined like this:
<telerik:RadRibbonView>
<telerik:RadRibbonView.QuickAccessToolBar>
<telerik:QuickAccessToolBar prism:RegionManager.RegionName="{x:Static inf:RegionNames.QuickAccessToolBarRegion}"/>
</telerik:RadRibbonView.QuickAccessToolBar>
The RegionManger will not be loads the region,
and when will replace the definition above(just for demo purposes) by this :
<telerik:RadRibbonView >
<ContentControl prism:RegionManager.RegionName="{x:Static inf:RegionNames.QuickAccessToolBarRegion}"/>
The RegionManager loads the region!
my question is:
what's Wrong in my XAML ? my be the region was defined inside a complex property?
Can you guide me,
thanks in advance.
best regards.
Solution :
Very good answer Brian, it works like a charm,
as Brian said:
first of all we name the targeted element:
<telerik:RadRibbonView.QuickAccessToolBar>
<telerik:QuickAccessToolBar x:Name="QuickAccessToolBar"/>
</telerik:RadRibbonView.QuickAccessToolBar>
in the code behind (the Shell in my case) :
public Shell(ShellViewModel viewModel, IRegionManager regionManager)
{
InitializeComponent();
DataContext = viewModel;
RegionManager.SetRegionName(QuickAccessToolBar,RegionNames.QuickAccessToolBarRegion);
RegionManager.SetRegionManager(QuickAccessToolBar, regionManager);
}
Where RegionNames.QuickAccessToolBarRegion ="QuickAccessToolBarRegion" and regionManager is the RegionManager resolved by unity container
Thank you very much Brian, good night :)
My guess is that the QuickAccessTolbar is not part of the visual tree, so the region manager can't find it. Since this is a Telerik control, I am not aware of how they architected their control. You might have to do this in code behind instead.
First set the region name, then set the RegionManager using the attached properties.
RegionManager.SetRegionName(quickToolbar, name);
RegionManager.SetRegionManager(quickToolbar, rm);
Scenario
Consider the following XAML inside a view of a Silverlight application:
<ListBox ItemsSource="{Binding Items}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" MouseLeftButtonDown="Item_Clicked" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The event handler is not called. Apparently, event handlers do not work inside DataTemplate.
There are no warnings or errors, neither at compile-time nor at runtime. Also, the MSDN DataTemplate reference does not specify any restrictions regarding event handlers.
IntelliSense even "helps" me with <New Event Handler> and Navigate to Event Handler. The handler is placed in the code behind of the view.
Questions
Is the behavior specified by Microsoft?
Is it a known bug in VS2010?
Why doesn't the compiler emit an error or
at least a warning?
Remark
I know how to solve the issue since the problem is reported quite often on this site. I want to know why this fails silently.
"Silent error" never gets you very far when your write .NET code, errors are never silent. I'll describe a general way you tackle issues like this, getting you to the "why" automatically and giving you a chance to google a solution.
An important implementation detail you need to know is how XAML is processed when your app is built. It is interpreted by a code generator, it auto-generates C# code which is stored in the obj\Debug directory of your project. After you build, you'll find *.g.cs files there. Take a look at them, you shouldn't have too much trouble making sense of them, the code that was auto-generated from the markup is annotated with #line directives.
Looking at the Silverlight version, MainPage.g.cs, shows little code (edited to fit):
public partial class MainPage : System.Windows.Controls.UserControl {
internal System.Windows.Controls.Grid LayoutRoot;
private bool _contentLoaded;
public void InitializeComponent() {
// etc..
}
}
No sign of the MouseLeftButtonDown event anywhere, this is not encouraging of course.
Do the exact same thing with a sample WPF app. Now take a look at MainWindow.g.cs (again edited to fit and remove the boring parts):
public partial class MainWindow : System.Windows.Window,
System.Windows.Markup.IComponentConnector,
System.Windows.Markup.IStyleConnector {
private bool _contentLoaded;
public void InitializeComponent() {
//...
}
void System.Windows.Markup.IComponentConnector.Connect(int connectionId, object target) {
//...
}
void System.Windows.Markup.IStyleConnector.Connect(int connectionId, object target) {
switch (connectionId)
{
case 1:
#line 9 "..\..\MainWindow.xaml"
((System.Windows.Controls.TextBlock)(target)).MouseLeftButtonDown += new System.Windows.Input.MouseButtonEventHandler(this.Item_Clicked);
//...
break;
}
}
Big difference, now you do see the MouseLeftButtonDown event back. Clearly the IStyleConnector interface is instrumental to get that event subscribed.
So go have a look at the MSDN article for that interface. It is rather brief, this is internal plumbing that you shouldn't have to know about, description is:
Provides methods used internally by the WPF XAML parser to attach events and event setters in compiled XAML.
A complete match for what you are trying to accomplish of course. Most important in that article is the Version Information section. Supported in the .NET Framework, but Silverlight is not listed.
This gives you the "why", the necessary plumbing is simply not available in Silverlight. Otherwise not surprising, the most important attribute of Silverlight is that it is small, allowing for a quick download that doesn't slow down the user too much when he browses to a web page that uses it. It can only get small by removing stuff.
So, apparently you are supposed to provide that plumbing yourself. How do you do this? The inspired google query is silverlight datatemplate event. The very first hit is a winner, a question that tries to do exactly what you are doing. Lots more relevant hits available if you need more help.
I have a WPF application that is utilizing the reporting tools included with Visual Studio 2010. I've had some other problems that I've solved by creating a graph of objects that are all marked as serializable, etc., as mentioned on various other web pages.
The ReportViewer control is contained in a WindowsFormsHost. I'm handling the SubreportProcessing event of the ReportViewer.LocalReport object to provide the data for the sub report.
The object graph that I'm reporting on is generated in my viewmodel, and that viewmodel holds a reference to it. The SubreportProcessing handler is in my code behind of my window (may not be the best place - but I simply want to get the ReportViewer working at this point).
Here's the problem: In my event handler, I'm attempting to get a reference to my viewmodel using the following code:
var vm = DataContext as FailedAssemblyReportViewModel;
When the handler is called, this line throws an InvalidOperationException with the message The calling thread cannot access this object because a different thread owns it.
I didn't realize the handler might be called on a different thread. How can I resolve this?
I attempted some searches, but all I've come up with is in regards to updating the UI from another thread using the Dispatcher, but that won't work in this case...
I solved this problem using something I believe is a hack, by adding the following function:
public object GetDataContext() {
return DataContext;
}
And then replacing the line of code from my question with:
object dc = Dispatcher.Invoke(new Func<object>(GetDataContext), null);
var vm = dc as FailedAssemblyReportViewModel;
However, this seems like a hack, and I might be circumventing some sort of safety check the CLR is doing. Please let me know if this is an incorrect way to accomplish this.
That's a nasty problem you have there.
Why don't you use in the view a content presenter which you bind to a windows form host?
And in the view model you would have a property of type of type WindowsFormsHost. Also,in the view model's constructor you could set the windows form's host Child property with the report viewer.
After that is smooth sailing, you could use your report viewer anywhere in your code. Something like this:
View:
<ContentPresenter Content="{Binding Path=FormHost}"/>
ViewModel:
private ReportViewer report = new ReportViewer();
private WindowsFormsHost host = new WindowsFormsHost();
public WindowsFormsHost FormHost
{
get {return this.host;}
set
{
if(this.host!=value)
{
this.host = value;
OnPropertyChanged("FormHost");
}
}
}
public ViewModel() //constructor
{
this.host.Child = this.report;
}
After that happy coding. Hope it helps.
I have a ViewModel which contains a Boolean property which tells you if the user has authenticated or not.
I have a WrapPanel which is bound to a collection of profiles. The DataTemplate for these profiles has an icon - a closed padlock for when the user is not authenticated and an open one for when the user is authenticated. Ideally these would be bound to the Boolean on the ViewModel but the DataContext for the templates is the individual profile objects.
I have tried,
Setting the Source selector in the binding as specified here although it appear Windows Phone 7 does not support x:Reference
I tried also the Inversion of Control(?) method detailed here (but containerLocator was not found on my object)
I tried applying a Style.Trigger but these are not supported in Windows Phone 7
I also tried accessing the XAML elements in the code behind and updating programmatically on event triggers, however I could not get a handle on the Image element inside the DataTemplate
Edit after comment: WP7 does not support style triggers. But if anyone is looking for this answer on following versions I let the reply below:
I would use a Style Trigger as seen here to update the icon Source property on the fly - as part of the style of your DataTemplate so you would get a hold of your Image.
One way I found that works based on an answer by Damian Antonowicz but does not implement the full inversion of control method that he uses, is as follows,
Create a partial class which resolves to your view-model instance under your view-model namespace, e.g.
public partial class ViewModelInstanceLocator
{
public AppViewModel AppViewModel // Or whatever the type of your view-model is ...
{
get
{
return App.VM; // Or wherever your view model instance is ...
}
}
}
Define the other half of the class in your XAML page as a resource so that it can be referred to as a static resource, I did this in my App.xaml so that it could be referred to everywhere,
<ResourceDictionary>
<viewmodel:ViewModelInstanceLocator x:Key="ViewModelInstanceLocator" />
...
</ResourceDictionary>
You may need to include the relevant namespace if there is not already a reference to your view-model namespace e.g. at the top,
xmlns:viewmodel="clr-namespace:MyAppNamespace.ViewModel"
Finally to bind to the view-model as follows,
{Binding AppViewModel.SomeProperty, Source={StaticResource ViewModelInstanceLocator}}
The binding updates as usual just as if the view-model instance had been referred to through the DataContext. However, it does not work with design-time data.
I am making a proof of concept WPF application with Prism 4 and Unity, but I run into some basic problems.
In our solution we have the following projects:
-AppName.Desktop
-AppName.Modules.ModuleA
-AppName.Modules.ModuleB
Having followed some tutorials, explored some examples and searched the internet, I wasn't able to find a suitable answer to a very rudimentary question; how can I switch between two views in different DLL's.
The first view (ModuleAView) is loaded in the Initialize method of ModuleA:
public void Initialize()
{
regionManager.RegisterViewWithRegion("MainRegion", typeof(Views.ModuleAView));
}
When I click in ModuleAView (or on a button in ModuleAView) I want to switch to ModuleBView.
public bool SomeEventInModuleAView(SomeEventParams e)
{
Uri viewNav = new Uri("ModuleBView", UriKind.Absolute);
regionManager.RequestNavigate(RegionNames.MainRegion, viewNav);
}
Obviously this won't work, since ModuleA doesn't know where to find ModuleBView. I have read about changing the URI with pack/application/component etc, but I can't seem to make it work.
I was thinking something like this:
Uri("pack://application:,,,/AppName.Modules.ModuleB;component/Views/ModuleBView.xaml", UriKind.Absolute);
Since loading modules from different assemblies is one of the purposes of Prism, I think it's strange that there aren't any examples in the Prism download to show how this works.
I have found the answer. I totally forgot to register the second view.
Check the solution over here: http://compositewpf.codeplex.com/discussions/402860#post940396