[UWP][C++/CX] Cannot access XAML UI from codebehind - c++-cli

C++:
#include "pch.h"
#include "MainPage.xaml.h"
using namespace Testing;
using namespace Platform;
using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Controls;
using namespace Windows::UI::Xaml::Controls::Primitives;
using namespace Windows::UI::Xaml::Data;
using namespace Windows::UI::Xaml::Input;
using namespace Windows::UI::Xaml::Media;
using namespace Windows::UI::Xaml::Navigation;
MainPage::MainPage()
{
InitializeComponent();
}
void Testing::MainPage::Page_SizeChanged(Platform::Object^ sender,Windows::UI::Xaml::SizeChangedEventArgs^ e)
{
SplitPane->isPaneOpen = !SplitPane->isPaneOpen; //LEGAL
}
void test()
{
SplitPane->isPaneOpen = !SplitPane->isPaneOpen; //NOT LEGAL
}
XAML:
<Page
x:Class="Testing.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Testing"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
SizeChanged="Page_SizeChanged">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<SplitView Name="SplitPane">
</SplitView>
</Grid>
So my question is why i cant use a reference or rather find the reference of the SplitView in my function? and is there a solution?
I find it a bit ugly to pass SplitPane as parameter
P.S. in C# test() is completely legal

In order to use the private fields of a MainPage, test() must be a method of the MainPage class.
In MainPage.xaml.h:
public ref class MainPage sealed
{
public:
MainPage();
void test();
};
In MainPage.xaml.cpp:
void MainPage::test()
{
SplitPane->IsPaneOpen = !SplitPane->IsPaneOpen; // LEGAL
}

Related

How to use Xaml inheritance in WinUI 3?

Judging from answers like this it looks like xaml inheritance works for WPF and UWP.
Does it work for WinUI 3?
Using code like this:
<local:YourBaseClass x:Class="MyApp.ChildClass"
...
xmlns:local="clr-namespace:MyApp">
</local:YourBaseClass>
I get the error:
Error WMC0001 Unknown type 'YourBaseClass' in XML namespace 'clr-namespace:MyApp'
Does it work for WinUI 3?
Yes. It almost exactly the same.
Create the base class that inherits from Microsoft.UI.Xaml.Window:
public class YourBaseClass : Microsoft.UI.Xaml.Window
{
public YourBaseClass() : base()
{
Title = "Title...";
}
}
Modify MainWindow.xaml.cs to inherit from the new base class:
public sealed partial class MainWindow : YourBaseClass
{
public MainWindow()
{
this.InitializeComponent();
}
private void myButton_Click(object sender, RoutedEventArgs e)
{
myButton.Content = "Clicked";
}
}
Modify the root element in MainWindow.xaml:
<local:YourBaseClass
x:Class="WinUI3App.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:WinUI3App"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
<Button x:Name="myButton" Click="myButton_Click">Click Me</Button>
</StackPanel>
</local:YourBaseClass>

How to use displaymemberpath on a combobox in UWP C++/CX?

So I'm trying to bind a combobox to a vector of myclass. myclass has a public Platform::String^ property called key. So in my XAML, I have displaymemberpath for the combobox set to "key". However, I just get a box of empty options, which is the same result as if i set displaymemberpath to "asdf" (a non-existent property). To illustrate my problem, I've tried to rewrite this tutorial on using displaymemberpath in C++/CX: https://asp-net-example.blogspot.com/2017/01/uwp-combobox-displaymemberpath-and.html (I've tried following the original tutorial in C# and it worked perfectly fine).
Here's my code:
(MainPage.xaml is exactly the same as the tutorial).
<Page
x:Class="displaymembercxxtest.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:displaymembercxxtest"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<StackPanel
x:Name="stack_panel1"
Orientation="Horizontal"
Background="LightYellow"
Padding="100"
>
<ComboBox
x:Name="ComboBox1"
Header="Select A Color"
DisplayMemberPath="ColorName"
SelectedValuePath="ColorValue"
SelectionChanged="ComboBox1_SelectionChanged"
>
</ComboBox>
<TextBlock
x:Name="TextBlock1"
FontFamily="Consolas"
FontSize="25"
Foreground="DarkGreen"
Margin="50,5,5,5"
/>
</StackPanel>
</Page>
Code Behind:
//MainPage.xaml.h
namespace displaymembercxxtest
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public ref class MainPage sealed
{
public:
MainPage();
private:
void ComboBox1_SelectionChanged(Platform::Object^ sender, Windows::UI::Xaml::Controls::SelectionChangedEventArgs^ e);
};
public ref class color sealed
{
public:
property Platform::String^ ColorName;
property Platform::String^ ColorValue;
color(Platform::String^ name, Platform::String^ value)
{
ColorName = name;
ColorValue = value;
}
};
}
   
//MainPage.xaml.cpp
MainPage::MainPage()
{
InitializeComponent();
Platform::Collections::Vector<color^>^ colors = ref new Platform::Collections::Vector<color^> ();
colors->Append(ref new color("INDIANRED", "#CD5C5C"));
colors->Append(ref new color("SALMON", "#FA8072"));
colors->Append(ref new color("CRIMSON", "#DC143C"));
colors->Append(ref new color("DEEPPINK", "#FF1493"));
colors->Append(ref new color("CORAL", "#FF7F50"));
colors->Append(ref new color("ORANGE", "#FFA500"));
colors->Append(ref new color("YELLOW", "#FFFF00"));
colors->Append(ref new color("PEACHPUFF", "#FFDAB9"));
ComboBox1->ItemsSource = colors;
}
void displaymembercxxtest::MainPage::ComboBox1_SelectionChanged(Platform::Object^ sender, Windows::UI::Xaml::Controls::SelectionChangedEventArgs^ e)
{
// not relevant to the problem
}
Here is what I'm seeing:
Here is what it looks like when I built the tutorial in C#:
As you can see, the options are blank in the C++/CX version. What am I doing wrong?
If you're using {Binding}, you'll need to add the BindableAttribute attribute to the color class.
Please check the following code:
[Windows::UI::Xaml::Data::Bindable]
public ref class color sealed
{
public:
property Platform::String^ ColorName;
property Platform::String^ ColorValue;
color(Platform::String^ name, Platform::String^ value)
{
ColorName = name;
ColorValue = value;
}
};
More information, please read Data binding overview documentation.

Can't get access to controls from code behind in pcl part of xamarin forms project

I've got Xamarin.Forms project with pcl-part and native win, ios and android parts.
All page structure and view-models are in pcl-part. App work's fine, but when I'm trying for example to hide Grid from code behind - it do nothing. Here is code example:
Xaml:
<ContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="SomeNamespase.SomePage">
<Grid x:Name="InnerGrid" BackgroundColor="Green">
<Frame x:Name="InnerContent"/>
</Grid>
</ContentPage>
.cs :
using System;
namespace SomeNamespase
{
public partial class SomePage : ContentPage
{
public void SomeMethod()
{
this.InnerGrid.IsVisible = false;
this.InnerContent.BackgroundColor = Color.Aqua;
}
}
}
I've also tried this.FindByName<Grid>("InnerGrid"); the same result
Note: if I am trying to get controls from action in PCL everything is good. Nothing going on when I'm trying to get controls from ViewPresenter in windows (or other platforms) project.
You need to make sure you are properly implementing INotifyPropertyChanged
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Please try the below code, as in your code I can't see the constructor.
using System;
namespace SomeNamespase
{
public partial class SomePage : ContentPage
{
public SomePage()
{
SomeMethod() ;
}
public void SomeMethod()
{
this.InnerGrid.IsVisible = false;
this.InnerContent.BackgroundColor = Color.Aqua;
}
}
}

UserControl InitializeComponents() method set the Content property, but does NOT trigger binding update

For example, there is a class MyUserControlBase derived from UserControl and within its constructor there is set of binding for Content dependency property.
MyUserControlBase.cs
namespace BindingBeforeInitComp
{
using System.Diagnostics;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Data;
public class MyUserControlBase : UserControl
{
public static readonly DependencyProperty MyContentProperty = DependencyProperty.Register("MyContent",
typeof(object),
typeof(MyUserControl),
new PropertyMetadata(null, MyContentChangedCallback));
public MyUserControlBase()
{
// Set binding to the Content property.
var propertyPath = new PropertyPath("Content");
var binding = new Binding { Path = propertyPath, Source = this };
SetBinding(MyContentProperty, binding);
}
public object MyContent
{
get { return GetValue(MyContentProperty); }
set { SetValue(MyContentProperty, value); }
}
private static void MyContentChangedCallback(
DependencyObject dependencyObject,
DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
Debugger.Break(); // Breaking here to show that binding to Content property has updated.
}
}
}
There is also MyUserControl(cs+xaml) derived from MyUserControlBase. In the constructor there is default IntializeComponents() method (Note: binding was set before this method in the base constructor). Before calling of IntializeComponents() the Content property is null, after calling Content is set to content described in xaml. But binding does not update a target property.
MyUserControl.xaml.cs
namespace BindingBeforeInitComp
{
using System.Diagnostics;
using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
public sealed partial class MyUserControl : MyUserControlBase
{
public MyUserControl()
{
Debugger.Break(); // Here Content is null.
InitializeComponent();
Debugger.Break(); // Here Content is set, but as you can see there is no binding update.
}
private void ChangeContentClick(object sender, RoutedEventArgs e)
{
// Change content by a click. In this case the property changing should trigger update.
Content = new Grid() { Background = new SolidColorBrush(Colors.Chartreuse) };
}
}
}
MyUserControl.xaml
<local:MyUserControlBase
x:Class="BindingBeforeInitComp.MyUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:BindingBeforeInitComp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<Grid Background="OrangeRed">
<Button Click="ChangeContentClick" HorizontalAlignment="Center" Width="200" Height="150" Background="#FF00AE4F">Change content</Button>
</Grid>
</local:MyUserControlBase>
Full sources: https://github.com/inTagger/Bugs/tree/master/BindingBeforeInitComp
UPDATE: I have updated sources on the GitHub, added WPF (NET45) project to demonstrate the true way how dependency properties and bindings should work. And yes, WPF has no such problem (behavior/feature/bug).
UPDATE2: If Content dependency property is set directly in constructor - binding works, but not if Content is set somewhere inside InitializeComponents().
The InitializeComponent method's job is to load your Xaml, so it's to be expected that none of your UI elements are available until after it has run.
Consider handling the Loaded event - you can attach a handler to that in your base constructor, and it won't be raised until after the object is fully constructed.

How can I specify a ViewModel to a XAML window?

I am trying to share a ViewModel between XAML windows. This is necessary to allow multiple views of the object instance to receive events from the ViewModel.
Specifying the ViewModel as a resource in the XAML, then overwriting it in an alternate constructor does not work. The binding will still be to the default instance created in the default constructor and will not receive events from or update the proper instance.
This does not work:
MyWindow.xaml:
<Window x:Class="MyNamespace.MyWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyNamespace"
Title="My Window"
Width="700" Height="550">
<Window.Resources>
<local:MyViewModel x:Key="MyModel"/>
</Window.Resources>
<ContentPresenter Content="{StaticResource MyModel}"/>
</Window>
MyWindow.xaml.cs
imports ...;
namespace MyNamespace {
public partial class MyWindow {
public MyWindow() {
InitializeComponent();
}
public MyWindow(MyViewModel model)
: this() {
Resources["MyModel"] = model;
}
}
}
Nor will this:
MyWindow.xaml.cs
imports ...;
namespace MyNamespace {
public partial class MyWindow {
public MyWindow()
: this(new MyViewModel()) { }
public MyWindow(MyViewModel model) {
Resources["MyModel"] = model; // Resources not yet initialized!
InitializeComponent();
}
}
}
if you are using Microsoft.Practices.Unity you can use TransientLifetimeManager.It will make sure that only one object of your viewmodel is created.
MyThis can be done by using properties on the code-behind and using the Binding tag in the XAML rather than StaticResource as follows:
MyWindow.xaml:
<Window x:Class="MyNamespace.MyWindow"
x:Name="this"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyNamespace"
Title="My Window"
Width="700" Height="550">
<ContentPresenter Content="{Binding MyModel, ElementName=this}"/>
</Window>
MyWindow.xaml.cs
imports ...;
namespace MyNamespace {
public partial class MyWindow {
public MyViewModel MyModel { get; private set; }
public MyWindow()
: this(new MyViewModel()) { }
public MyWindow(MyViewModel model) {
MyModel = model;
InitializeComponent();
}
}
}
Multiple windows (or other components) can use the same model instance.
Edit 06-Dec-12:
The XAML was not correct and the binding would not work. Added the x:Name attribute to the root element (Window), and added the ElementName argument to the Content attribute of the bound element (ContentPresenter).