mercredi 16 décembre 2020

Propagating a VisualState of your ListViewItemPresenter to your DataTemplate using the Parent element [XAML]

In this Article we are going to see how to Propagate a VisualState of your ListViewItemPresenter to your DataTemplate using the Parent element

My need was that I needed to be able to pass down the information that an item was selected so that I could have the round blue border around the icon.  One solution would have been to add a isSelected property to my header items however I wished something simple to manage the selected state of my header.

Here is a snapshot of what I wanted:



In the end you have two options to propagate the VisualState.  First you can use this option which I found on Microsoft Docs 

Visual states for elements that aren't controls

Visual states are sometimes useful for scenarios where you want to change the state of some area of UI that's not immediately a Control subclass. You can't do this directly because the control parameter of the GoToState method requires a Control subclass, which refers to the object that the VisualStateManager acts upon. Page is a Control subclass, and it's fairly rare that you'd be showing UI in a context where you don't have a Page, or your Window.Content root isn't a Control subclass. We recommend you define a custom UserControl to either be the Window.Content root or be a container for other content you want to apply states to (such as a Panel ). Then you can call GoToState on your UserControl and apply states regardless of whether the rest of the content is a Control. For example you could apply visual states to UI that otherwise consists of just a SwapChainPanel so long as you placed that within your UserControl and declared named states that apply to the properties of the parent UserControl or of the named SwapChainPanel part of the template.

Source: https://docs.microsoft.com/en-us/uwp/api/windows.ui.xaml.visualstatemanager?view=winrt-19041

However this was a total fail for me as it never worked and also seemed overkill to set a Usercontrol inside my DataTemplate, I do think that with a bit more work I could have gotten it to work but belived that a simpler solution was possible for my needs.

Binding to the ParentElement

In the end the solution that I found that worked the best for my needs was to bind the element to its parent so that when the parent knew that its item was being selected I could pass down this information to my the DataTemplate. The idea was to bind the BorderThickness of my elements in my DataTemplate to it ParentElement, in xaml this means:

BorderThickness="{Binding BorderThickness, RelativeSource={RelativeSource TemplatedParent}}"

Here my TemplatedParent is the ListViewItemPresenter that is located in my ListView. And with this I was able to pass the different states values Selected/UnSelected to my DataTemplate.  I do agree that If i was able to pass all the different states to my DataTemplate that would have been better but this is the next best thing for me.

Here is my DataTemplate:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<DataTemplate x:Key="KeyHeaderFollowingItemTemplate" x:DataType="m:HeaderItemViewModel">
	<Grid
		x:Name="RootGrid"
		Width="235"
		Height="65"
		Margin="4"
		HorizontalAlignment="Stretch"
		VerticalAlignment="Stretch">
		<Grid.RowDefinitions>
			<RowDefinition Height="65" />
		</Grid.RowDefinitions>

		<StackPanel HorizontalAlignment="Left" Orientation="Horizontal">
			<Grid>
				<Border
					Height="58"
					Margin="0,0,12,0"
					Background="Transparent"
					BorderBrush="{StaticResource DmLightBlueBrush}"
					BorderThickness="{Binding BorderThickness, RelativeSource={RelativeSource TemplatedParent}}"
					CornerRadius="5">
					<Grid>
						<controls:ImageEx
							x:Name="HeaderImg"
							Grid.Row="0"
							Height="51"
							Margin="0,0,0,0"
							HorizontalAlignment="Center"
							VerticalAlignment="Center"
							BorderThickness="{Binding BorderThickness, RelativeSource={RelativeSource TemplatedParent}}"
							CornerRadius="4"
							Source="{Binding HeaderImg}"
							Stretch="UniformToFill" />
					</Grid>
				</Border>
			</Grid>

			<TextBlock
				Name="TitleTxt"
				Width="160"
				Margin="0,8,0,8"
				VerticalAlignment="Center"
				FontFamily="Retina"
				FontSize="16"
				FontWeight="Bold"
				LineHeight="16"
				Text="{Binding HeaderTitle, FallbackValue=Loading}"
				TextTrimming="WordEllipsis"
				TextWrapping="Wrap" />
		</StackPanel>
	</Grid>
</DataTemplate>

Happy Coding!


mercredi 2 décembre 2020

UWP Entity framework update your DB schema with a new column [UWP][C#][Database]

The point of this article is to go over the different steps that were needed to add a new column to one of my existing table.  This table was already used by my users using my application called My Stocks Alerts & Charts.

To add a new column or table to your existing DB you are going to need to create 3 new files.  The first new class will be called MyAlertQuotesModelSnapshot which inherit from ModelSnapshot and have the annotation of DbContext, this will hold the new table schema model of our updated table.

Next, we will create a class called MyFirstMigration this will also hold you new table schema model, this class will not inherit from anything but will have the annotations of DbContext and Migration.

Lastly the most important part we are going to create the class that will add the column to our table this will be called MyFirstMigration will inherit from Migration and will allow us to add our new column to our table by using migrationBuilder.AddColumn as follows:

migrationBuilder.AddColumn<string>(
                            name: "ExtraColumn",
                            table: "MyAlertQuotes",
                            nullable: true);

If you had wanted to add a new table that was called MyAlertQuotes we would have used migrationBuilder.CreateTable as follows:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
protected override void Up(MigrationBuilder migrationBuilder)
{         

	migrationBuilder.CreateTable(
		name: "MyAlertQuotes",
		columns: table => new
		{
			Id = table.Column<Guid>(nullable: false),
			SymbolId = table.Column<string>(nullable: false),
			FullName = table.Column<string>(nullable: false),
			Currency = table.Column<string>(nullable: false),
			ExtraColumn = table.Column<string>(nullable: false),
			 
		},
		constraints: table =>
		{
			table.PrimaryKey("PK_MyAlertQuotes", x => x.Id);
		});
}

Which would have added a new table to our DbSchema. 

One last step, you will also have a class that inherits from DbContext, make you that you add check on initialization to make sure your users have migrated to the new Db schema

1
2
3
4
5
6
7
 public static void CheckMigrations()
{
	using (var db = new LocalStorageContext())
	{
		db.Database.Migrate();
	}
}

Personally in my App.xaml.cs I check to make sure that users have updated to my new Db Schema.

Here are my full classes:

20201101_MyFirstMigration.cs

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using MyPersonalStocks.Helpers;
using System;

namespace MyPersonalStocks.Common.DataBase.Migrations
{
    public partial class MyFirstMigration : Migration
    {
        protected override void Up(MigrationBuilder migrationBuilder)
        {

            migrationBuilder.AddColumn<string>(
                            name: "ExtraColumn",
                            table: "MyAlertQuotes",
                            nullable: true);
        }

        protected override void Down(MigrationBuilder migrationBuilder)
        {
            //migrationBuilder.DropColumn(
            //               name: "ExtraColumn",
            //               table: "MyAlertQuotes"
            //               );
        }
    }
}


20201101_MyFirstMigration.design.cs
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using MyPersonalStocks.Helpers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyPersonalStocks.Common.DataBase.Migrations
{
    [DbContext(typeof(LocalStorageContext))]
    [Migration("20201101_MyFirstMigration")]
    partial class MyFirstMigration
    {
        protected override void BuildTargetModel(ModelBuilder modelBuilder)
        {
            modelBuilder
                .HasAnnotation("ProductVersion", "1.0.4");

            modelBuilder.Entity("MyPersonalStocks.Model.DbModel.AlertQuotesDb", b =>
            {
                b.Property<Guid>("Id");

                b.Property<string>("SymbolId");
                b.Property<string>("FullName");
                b.Property<string>("Currency");
                b.Property<string>("ExtraColumn");

                b.HasKey("Id");

                b.ToTable("MyAlertQuotes");
            });
        }
    }
}


MyAlertQuotesModelSnapshot.cs
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using MyPersonalStocks.Helpers;
using MyPersonalStocks.Model.DbModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyPersonalStocks.Common.DataBase.Migrations
{


    [DbContext(typeof(LocalStorageContext))]
    partial class MyAlertQuotesModelSnapshot : ModelSnapshot
    {
        protected override void BuildModel(ModelBuilder modelBuilder)
        {
            modelBuilder
                .HasAnnotation("ProductVersion", "1.0.4");

            modelBuilder.Entity("MyPersonalStocks.Model.DbModel.AlertQuotesDb", b =>
            {
                b.Property<Guid>("Id");

                b.Property<string>("SymbolId");
                b.Property<string>("FullName");
                b.Property<string>("Currency");
                b.Property<string>("ExtraColumn");
                b.HasKey("Id");

                b.ToTable("MyAlertQuotes");
            });
        }
    }

}