How to use x:bind inside ControlTemplate in UWP - xaml

Hi I was just trying to use x:Bind in ControlTemplate of Button in UWP my simple code is below,
<Grid>
<TextBox x:Name="txtWidth"/>
<Button x:Name="btnEllipse" PointerEntered="btnEllipse_PointerEntered" PointerExited="btnEllipse_PointerExited" Click="btnEllipse_Click">
<Button.Template>
<ControlTemplate>
<Ellipse x:Name="myEll" Width="{x:Bind ShapeWidth,Mode=OneWay}" Height="{Binding Width,ElementName=myEll}" Fill="Purple" Stroke="Black" StrokeThickness="2" />
</ControlTemplate>
</Button.Template>
</Button>
</Grid>
C# Code behind file
double _shapeWidth= 100;
public Double ShapeWidth
{
get { return _shapeWidth; }
set { _shapeWidth = value; }
}
I am getting error that
target type' is required to use x:Bind inside a ControlTemplate
Please let me know where I am making mistake,?
Another scenario can we bind txtWidth using Binding or x:Bind in Ellipse Width here?

Based on this document, when you try to use x:bind in ControlTemplate, you need to add the TargetType property(e.g. <ControlTemplate TargetType="Button">). However, The function of x:bind is like TemplateBinding, it can only bind with the property of Button, so if you want to bind with the property which declared in code-behind, it's better to use Binding and declare the DataContext. For example:
.xaml:
<Button x:Name="btnEllipse" PointerExited="btnEllipse_PointerExited" Click="btnEllipse_Click">
<Button.Template>
<ControlTemplate>
<Ellipse x:Name="myEll" Width="{Binding ShapeWidth}" Height="{Binding Width,ElementName=myEll}" Fill="Purple" Stroke="Black" StrokeThickness="2" />
</ControlTemplate>
</Button.Template>
</Button>
.cs:
this.DataContext = this;
Another scenario can we bind txtWidth using Binding or x:Bind in
Ellipse Width here?
If you want to bind the width of txtWidth with the width of Ellipse, you could use ElementName to find the txtWidth element and use binding to bind with its width.
<TextBox x:Name="txtWidth" Width="100" Text="efwiehfiweh"/>
<Button x:Name="btnEllipse" PointerExited="btnEllipse_PointerExited" Click="btnEllipse_Click">
<Button.Template>
<ControlTemplate>
<Ellipse x:Name="myEll" Width="{Binding Width,ElementName=txtWidth,Mode=OneWay}" Height="{Binding Width,ElementName=myEll}" Fill="Purple" Stroke="Black" StrokeThickness="2" />
</ControlTemplate>
</Button.Template>
</Button>

Related

Xaml Internal Error error WMC9999: Object reference not set to an instance of an object

This has come up a few times, and I've been struggling with it all day in a UWP app.
My specific issue was that I was using x:Bind inside a ContentTemplate that was inside a DataTemplate:
<DataTemplate x:DataType="IFactionMember">
<Button Command="{x:Bind **Property of IFactionMember**}"> // Good
<Button.Template>
<ControlTemplate TargetType="Button">
<Grid Padding="10,0">
<TextBlock Text="{x:Bind **Property of IFactionMember**}" /> // Bad
</Grid>
</ControlTemplate>
</Button.Template>
</Button>
</DataTemplate>
You cannot do this :(
You could use {Binding} instead for your scenario, for example:
<ListView x:Name="listview" ItemsSource="{x:Bind members}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:IFactionMember">
<Button >
<Button.Template>
<ControlTemplate TargetType="Button" >
<Grid Padding="10,0">
<TextBlock Text="{Binding testtext}" />
</Grid>
</ControlTemplate>
</Button.Template>
</Button>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
x:bind lacks some of the features of {Binding}, in x:Bind, Path is rooted at the Page by default, not the DataContext, it seems like if you are using x:bind here, it will try to find the property at the MainPage, so that it will not find the correct property.

UWP - How to bind a ContentPresenter's element to the main ViewModel?

I work on an app where the user can edit a "big" form:
The form contains a lot of fields: it is displayed in a Pivot, where each PivotItem represents a category of the form.
Some categories contain one or more child items: they are displayed through a Master-Detail presentation.
To display the items of the Detail part, I use a ContentPresenter that is binded directly to the Model:
<ContentPresenter
x:Name="DetailContentPresenter"
Grid.Column="1"
Grid.RowSpan="2"
BorderThickness="1,0,0,0"
Padding="24,0"
BorderBrush="{ThemeResource SystemControlForegroundBaseLowBrush}"
Content="{x:Bind MasterListView.SelectedItem, Mode=OneWay}">
<ContentPresenter.ContentTemplate>
<DataTemplate x:DataType="models:Form_Parts">
<...>
</DataTemplate>
</ContentPresenter.ContentTemplate>
</ContentPresenter>
This works fine, but there is a "third" level in the main form: a list of photos, that I display in the ContentPresenter through a GridView. This works also great, but I would like to use an "Add" button or the "ItemClick" event to interact with the photos: I don't see how I could bind a Command in the main ViewModel, instead of the ContentPresenter's DataContext. The other fields of this form bind well on this ViewModel.
I did some tests without any result:
<StackPanel Orientation="Horizontal" Grid.Row="1">
<GridView ItemsSource="{Binding images}"
ItemClick="{Binding PhotoClickCommand}">
<GridView.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Gray" BorderThickness="1"
Padding="10"
Height="150" Width="190">
<Image Stretch="UniformToFill"
Source="{Binding bitmap_image}" />
</Border>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
<Border BorderBrush="Gray" BorderThickness="1"
Padding="10"
Height="150" Width="190">
<Button Command="{Binding Path=DataContext.AddPhotoCommand, ElementName=DetailsPage}"
Height="100" Width="100">
<Viewbox>
<SymbolIcon Symbol="Add"/>
</Viewbox>
</Button>
</Border>
</StackPanel>
=> is there a way to doing this?
Edit :
I solved my problem for the "Add" button thanks Alteik with:
<Button Command="{Binding ElementName=Root, Path=DataContext.AddPhotoCommand}" Height="100" Width="100">
But I always get a problem with the ItemClick of the GridView. I tried this:
<GridView ItemsSource="{Binding images}"
IsItemClickEnabled="True"
SelectionMode="None">
<i:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Tapped">
<core:InvokeCommandAction Command="{Binding ElementName=Root, Path=DataContext.EditPhotoCommand}"
CommandParameter="{Binding Mode=OneWay}"/>
</core:EventTriggerBehavior>
</i:Interaction.Behaviors>
<...>
</GridView>
But I don't get the expected parameter in the ViewModel:
private void EditPhoto(object sender)
{
Images sImage = (Images)sender;
if (sImage != null)
{
NavigationService.NavigateTo<CommentImageViewModel>(this, new Object[] { sImage }, true);
}
}
The sender is the current Form_Parts (the DataTemplate of the ContentPresenter), whereas I would get the selected item of my images collection, that is binded to the GridView...
=> Whad could I do to fix this?
You should be able to change the DataContext in a child element doing this:
Command"{Binding ElementName=Root, Path=DataContext.TheCommandFromRoot}"
Obviously you need to set the name of the parent's element to "Root"

How to do relative binding for button flyout in universal windows store app

I want to bind my button flyout width and height with some other control but it not working
<Grid >
<Popup IsOpen="True" Name="popup">
<Grid Name="popupBorder" Background="Red" Height="100" Width="100" >
</Grid>
</Popup>
<Button Content="check flyout" Name="btn" Click="Button_Click" >
<Button.Flyout>
<Flyout>
<Border Name="flyoutBorder" Height="{Binding Path=Height, ElementName=popupBorder, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}"
Width="{Binding Path=Width, ElementName=popupBorder, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}">
</Border>
</Flyout>
</Button.Flyout>
</Button>
</Grid>
I have also given datacontext to button but still same
DataContext="{Binding ElementName=localContext, Mode=OneWay}"
When you bind data in the content of Flyout, the binding source is in Page, and binding target is in PopupRoot, they have different DataContext, so can't it work here.
Besides, a Flyout control is like this:
If you place a Border as Content of Flyout, it will be placed in the ScrollContentPresenter:
As you can see, if you set the Width and Height of content, it will not effect the size of the Flyout since there is a ScrollViewer inside it: ScrollViewer's content has unlimited height and width.
I want to bind my button flyout width and height with some other control but it not working
So, the right way to custom the size of Flyout is to style the FlyoutPresenter for example like this:
<Flyout>
<Flyout.FlyoutPresenterStyle>
<Style TargetType="FlyoutPresenter">
<Setter Property="MinHeight" Value="100" />
<Setter Property="MinWidth" Value="100" />
</Style>
</Flyout.FlyoutPresenterStyle>
<Border Name="flyoutBorder" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
</Border>
</Flyout>
Here we need to use MinHeight and MinWidth to set the size of Flyout, but since you want to bind Width and Height to some other control, and the Windows Runtime doesn't support a Binding usage for Setter.Value (the Binding won't evaluate and the Setter has no effect, you won't get errors, but you won't get the desired result either).
Usually when need data binding in Setter.Value, we can create some attached dependency properties. And to solve the different DataContext problem, we need to define the property in code behind or view model for example like this:
public static double NewWidth { get; set; } = 100.0;
Then bind the this property to the Width of popupBorder:
<Grid>
<Popup IsOpen="False" Name="popup">
<Grid Name="popupBorder" Background="Red" Height="100" Width="{Binding NewWidth}">
</Grid>
</Popup>
<Button Content="check flyout" Name="btn" Click="Button_Click" Foreground="Black">
<Button.Flyout>
<Flyout>
<Flyout.FlyoutPresenterStyle>
<Style TargetType="FlyoutPresenter">
<Setter Property="local:BindingBuilder.FlyoutWidth" Value="400" /> <!--This value has no meaning here, you can set it to any value.-->
<Setter Property="MinHeight" Value="100" />
</Style>
</Flyout.FlyoutPresenterStyle>
<Border Name="flyoutBorder" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
</Border>
</Flyout>
</Button.Flyout>
</Button>
</Grid>
To register attached property, you can for example code like this:
public class BindingBuilder : DependencyObject
{
public static readonly DependencyProperty FlyoutWidthProperty =
DependencyProperty.RegisterAttached("FlyoutWidth", typeof(double), typeof(BindingBuilder), new PropertyMetadata(0, OnFlyoutWidthChanged));
public static double GetFlyoutWidth(DependencyObject obj)
{
return (double)obj.GetValue(FlyoutWidthProperty);
}
public static void SetFlyoutWidth(DependencyObject obj, double value)
{
obj.SetValue(FlyoutWidthProperty, value);
}
public static void OnFlyoutWidthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
double newFlyoutWidth = (double)d.GetValue(FlyoutWidthProperty);
var presenter = (FlyoutPresenter)d;
presenter.MinWidth = MainPageViewModel.NewWidth;
}
}
Here I only attached the MinWidth property, you can also use the same method to custom the MinHeight of FlyoutPresenter.

How to make a Stack Panel Visible based on the Tapped Event of a ListView in Windows Phone 8.1?

In my application, I am having a ListView. ListView lists a set of images.
So when the application is running, and when that page is loaded, a list of images are shown.
<ListView ItemsSource="{Binding imageLists}" Background="Red" Tapped="ListView_Tapped">
<ListView.ItemTemplate>
<DataTemplate>
<Grid Background="Green">
<Image Source="{Binding imagePath}" CacheMode="BitmapCache" Stretch="UniformToFill"/>
<StackPanel Name="imageTitle" Visibility="Collapsed" Background="Blue"
HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock Foreground="White" Text="Dumy Image Title" FontSize="10"/>
</StackPanel>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
As you can see from the code, I have one image & stackpanel inside the listview. And the stackpanel's visibility has been set to collapsed for convenience.
The StackPanel imageTitle resides inside the ListView. Stackpanel contains a TextBlock housing the images name. For now it's dumy text.
On Tapping any image in the list, I am trying to make the stackPanel visible.
The Code Behind:
private void ListView_Tapped(object sender, TappedRoutedEventArgs e)
{
imageTitle.Visibility = Windows.UI.Xaml.Visibility.Visible;
}
Since the stackpanel is inside the listview & I am trying to make it visible on the tap event of the listview, I am not able to acheive the needed result. I know my code is wrong.
If I specify the stackpanel outside the listview, I can make it visible using the code I gave inside the ListView_Tapped function. But even in that case, I need to show the stackpanel (name of the image I clicked) inside the listview item (image I clicked).
Any help??
Can this be achieved using only XAML?
Here's a pure xaml way.
Rather than changing the Visibility of the imageTitle (not a great UX), let's change its Opacity to make its appearing more interesting.
First we need to create a storyboard inside this data template. This storyboard will fade in the imageTitle in 400ms.
And then we drag a ControlStoryboard behavior from Expression Blend's Asset panel onto the top level Grid. Basically we want the storyboard to fire off when this Grid is tapped.
Please see below code for reference.
<DataTemplate x:Key="GroupTemplate">
<Grid Background="Green">
<Grid.Resources>
<Storyboard x:Name="ShowImageTitleStoryboard">
<DoubleAnimation Duration="0:0:0.4" To="1" Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="imageTitle"/>
</Storyboard>
</Grid.Resources>
<Interactivity:Interaction.Behaviors>
<Core:EventTriggerBehavior EventName="Tapped">
<Media:ControlStoryboardAction Storyboard="{StaticResource ShowImageTitleStoryboard}"/>
</Core:EventTriggerBehavior>
</Interactivity:Interaction.Behaviors>
<Image Source="{Binding Image}" CacheMode="BitmapCache" Stretch="UniformToFill"/>
<StackPanel x:Name="imageTitle" Background="Blue"
HorizontalAlignment="Center" VerticalAlignment="Center" Opacity="0">
<TextBlock Foreground="White" Text="Dumy Image Title" FontSize="10"/>
</StackPanel>
</Grid>
</DataTemplate>
Apart from JustinXL's answer it can also be done by using ChangePropertyAction. Also pure XAML:
<DataTemplate>
<Grid Background="Green">
<Image Source="{Binding imagePath}" CacheMode="BitmapCache" Stretch="UniformToFill">
<i:Interaction.Behaviors>
<ic:EventTriggerBehavior EventName="Tapped">
<ic:ChangePropertyAction TargetObject="{Binding ElementName=imageTitle}" PropertyName="Visibility" Value="Visible"/>
</ic:EventTriggerBehavior>
</i:Interaction.Behaviors>
</Image>
<StackPanel Name="imageTitle" Visibility="Collapsed" Background="Blue"
HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock Foreground="White" Text="Dumy Image Title" FontSize="10"/>
</StackPanel>
</Grid>
</DataTemplate>
It's just another way to change a property - JustinXL's answer will provide nice animation with opacity, which will look much better.
I dunno why you specifically want to handle the scenario using XAML. For the Microsoft's recommended MVVM model you should bind a property to your element field and then you can write a converter for the same to return back "Visible" or "Collapse".

Custom pushpins and templating images

This is probably something simple but I am tearing my hair out at the moment.
I want to display pushpins on a map from a model. So I have created a template for the pushpins
<ControlTemplate x:Key="PushpinTemplate" TargetType="m:Pushpin">
<Grid x:Name="ContentGrid" Width="32" Height="32" Margin="0">
<Image Source="Resources/Pushpins/img.png" Stretch="Fill"/>
</Grid>
</ControlTemplate>
Then use it to a binded collection as follows:
<m:MapLayer x:Name="myPushpinLayer">
<m:MapItemsControl x:Name="myPushpins" ItemsSource="{Binding PushpinCollection}">
<m:MapItemsControl.ItemTemplate>
<DataTemplate>
<m:Pushpin Location="{Binding Location}" Template="{StaticResource PushpinTemplate}" />
</DataTemplate>
</m:MapItemsControl.ItemTemplate>
</m:MapItemsControl>
</m:MapLayer>
But what I want to be able to do is change the image source via a binding but am not sure how I go about doing this. What I was intending to do was to use a converter to change the image depending on an id within the collection if that alters the best way to do this.
Edit:
I got a little further:
<m:MapLayer x:Name="myPushpinLayer">
<m:MapItemsControl x:Name="myPushpins" ItemsSource="{Binding PushpinCollection}">
<m:MapItemsControl.ItemTemplate>
<DataTemplate>
<m:Pushpin Location="{Binding Location}"/>
<m:Pushpin.Template>
<ControlTemplate>
<Grid x:Name="ContentGrid" Width="32" Height="32" Margin="0">
<Image Source="{Binding Type,Converter={StaticResource ImageConverter}}" Stretch="Fill"/>
</Grid>
</ControlTemplate>
</m:Pushpin.Template>
</m:Pushpin>
</DataTemplate>
</m:MapItemsControl.ItemTemplate>
</m:MapItemsControl>
</m:MapLayer>
If I move the pushpin template into the </phone:PhoneApplicationPage.Resources> section then it fails. I am not sure why. I guess now I am trying to get to grips with my uinderstanding of how this all works
Just add the binding to the ControlTemplate
<ControlTemplate x:Key="PushpinTemplate" TargetType="m:Pushpin">
<Grid x:Name="ContentGrid" Width="32" Height="32" Margin="0">
<Image Source="{Binding ImageUri}" Stretch="Fill"/>
</Grid>
</ControlTemplate>
I would recommend a ImageUri (of type Uri) property that reflects what image to display, rather than using a converter. But a converter might also work, the bindings are done in the same way.