WPF Need help understanding how to turn into a template custom control - wpf

I am trying to design a layout for a custom control for a project I am working on and got it looking the way I would like it to look, the problem is that from functional point of view I don't believe it is going to work for what I really need it to do. The following can be dropped into a Window to get the look I am going for.
<Window x:Class="TestProject.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TestProject"
mc:Ignorable="d"
Title="MainWindow" Height="1080" Width="1920">
<StackPanel Margin="15,80,1535.333,85.667">
<Border BorderBrush="#FFD9D9D9" BorderThickness="0,1.5,0,0">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="35" />
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Grid.ColumnSpan="3" Background="#FFF1F1F1">
</StackPanel>
<StackPanel Grid.Column="1" Margin="0,1,20,3" VerticalAlignment="Center">
<TextBlock FontSize="18" FontWeight="SemiBold">Header</TextBlock>
</StackPanel>
<StackPanel Grid.Column="2" Margin="0,1,10,3" VerticalAlignment="Center">
<TextBlock HorizontalAlignment="Right" FontWeight="SemiBold" FontSize="18">$125,400.00</TextBlock>
</StackPanel>
<StackPanel Grid.Column="1" Grid.Row="1" Margin="0,2,20,2">
<TextBlock Foreground="#FF383838" FontSize="18" Margin="0,2">Test 1</TextBlock>
<TextBlock Foreground="#FF383838" FontSize="18" Margin="0,2">Test 2</TextBlock>
<TextBlock Foreground="#FF383838" FontSize="18" Margin="0,2">Test 3</TextBlock>
<TextBlock Foreground="#FF383838" FontSize="18" Margin="0,2">Test 4</TextBlock>
<TextBlock Foreground="#FF383838" FontSize="18" Margin="0,2">Test 5</TextBlock>
</StackPanel>
<StackPanel Grid.Column="2" Grid.Row="1" Margin="0,2,10,2">
<TextBlock HorizontalAlignment="Right" Foreground="#FF383838" FontSize="18" Margin="0,2">$0.00</TextBlock>
<TextBlock HorizontalAlignment="Right" Foreground="#FFBB0808" FontSize="18" Margin="0,2">$-100.00</TextBlock>
<TextBlock HorizontalAlignment="Right" Foreground="#FF383838" FontSize="18" Margin="0,2">$125,000.00</TextBlock>
<TextBlock HorizontalAlignment="Right" Foreground="#FF383838" FontSize="18" Margin="0,2">$1,000.00</TextBlock>
<TextBlock HorizontalAlignment="Right" Foreground="#FFBB0808" FontSize="18" Margin="0,2">$-500.00</TextBlock>
</StackPanel>
</Grid>
</Border>
</StackPanel>
</Window>
Now the issue I am struggling with is turning this is a template, that can be used with a binding, and I was thinking that I should maybe have gone with a listview and Grid View for the column headers. So any advice would be appreciated here as well.
The above has a few things I can see as an issue, mainly that I would need to click on each item row that will then run some code, and that is why I feel that I should have gone with a Grid View option instead. But the one reason I didn't go with the Grid View, was mainly because I could see no way to do the same layout here, with the columns.
So to recap
Should I have gone with a Grid View, if so how can I get the same results with the scaling of the middle column?
Best way to go about learning to turn this into a custom control, that I can style. Mainly looking for an explanation, resources to learn from.
Edit:
For those following or interested, the part I am struggling with is the following section. How would I do this from data binding, and get the template to be controlled to look this way? I understand the binding part, it's the actual template to control the data in the same way as the following code demonstrates.
<StackPanel Grid.Column="1" Grid.Row="1" Margin="0,2,20,2">
<TextBlock Foreground="#FF383838" FontSize="18" Margin="0,2">Test 1</TextBlock>
<TextBlock Foreground="#FF383838" FontSize="18" Margin="0,2">Test 2</TextBlock>
<TextBlock Foreground="#FF383838" FontSize="18" Margin="0,2">Test 3</TextBlock>
<TextBlock Foreground="#FF383838" FontSize="18" Margin="0,2">Test 4</TextBlock>
<TextBlock Foreground="#FF383838" FontSize="18" Margin="0,2">Test 5</TextBlock>
</StackPanel>
<StackPanel Grid.Column="2" Grid.Row="1" Margin="0,2,10,2">
<TextBlock HorizontalAlignment="Right" Foreground="#FF383838" FontSize="18" Margin="0,2">$0.00</TextBlock>
<TextBlock HorizontalAlignment="Right" Foreground="#FFBB0808" FontSize="18" Margin="0,2">$-100.00</TextBlock>
<TextBlock HorizontalAlignment="Right" Foreground="#FF383838" FontSize="18" Margin="0,2">$125,000.00</TextBlock>
<TextBlock HorizontalAlignment="Right" Foreground="#FF383838" FontSize="18" Margin="0,2">$1,000.00</TextBlock>
<TextBlock HorizontalAlignment="Right" Foreground="#FFBB0808" FontSize="18" Margin="0,2">$-500.00</TextBlock>
</StackPanel>

The reason to use a custom control is because you want to re-template it entirely for theming. They are more complicated than usercontrols or templating and if you don't need theming functionality then a usercontrol or template would be the way to go.
It does look like a listview would do what you're looking for.
You would want a row viewmodel which exposed a brush to bind foreground of the second column to.
And that column would look something like:
ForeBrush being a public solidcolorbrush.
Brushes are arguably a view responsibility so you could use an enum or some such instead. And a converter to translate that into a brush.
The whole of that markup could then be turned into a datatemplate and your data into a viewmodel. Use type of that viewmodel to template it into the ui via the datatype matching your viewmodel.
<DataTemplate DataType="{x:Type local:CostingsListViewModel}"
<ListView>
.....
Or you could put it in a UserControl
<DataTemplate DataType="{x:Type local:CostingsListViewModel}"
<local:CostingsListView/>

Related

Creating a Table in XAML and populating it with an Array?

If I have a table like the one below:
<Grid VerticalAlignment="Top" HorizontalAlignment="Left" ShowGridLines="True" Width="250" Height="100">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock FontSize="20" FontWeight="Bold" Grid.ColumnSpan="3" Grid.Row="0">2005 Products Shipped</TextBlock>
<TextBlock FontSize="12" FontWeight="Bold" Grid.Row="1" Grid.Column="0">Quarter 1</TextBlock>
<TextBlock FontSize="12" FontWeight="Bold" Grid.Row="1" Grid.Column="1">Quarter 2</TextBlock>
<TextBlock FontSize="12" FontWeight="Bold" Grid.Row="1" Grid.Column="2">Quarter 3</TextBlock>
<TextBlock Grid.Row="2" Grid.Column="0">50000</TextBlock>
<TextBlock Grid.Row="2" Grid.Column="1">100000</TextBlock>
<TextBlock Grid.Row="2" Grid.Column="2">150000</TextBlock>
<TextBlock FontSize="16" FontWeight="Bold" Grid.ColumnSpan="3" Grid.Row="3">Total Units: 300000</TextBlock>
</Grid>
Would I be able to populate it in one go using an array.
For example, if I had an Array containing "Row 1", "Row 2" etc. thru 10 would I be able to populate the first column with those values?
I'm not sure I'm doing a great job of explaining. I know I could do each cell individually, but I want it to cycle through and do all at once?
Thanks
Firstly, consider just using a ListBox or ItemsControl, with a DataTemplate. Define the 2 header rows in a separate grid, and stack this one underneath. The catch with this approach is that you need to define fixed-width columns, since each row will be its own Grid (or actually StackPanel is more performant in this scenario):
<ItemsControl ItemsSource="{Binding TheArray}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Width="100" Text="{Binding Col1}" />
<TextBlock Width="100" Text="{Binding Col2}" />
<TextBlock Width="100" Text="{Binding Col3}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Secondly, if you need to use a true Grid, then one approach could be to define a Behavior on your Grid. This Behavior class would define an Items dependency property. The dependency property's "changed" handler could then create the TextBlocks (x of them for each cell where x is the number of columns), add them to the Grid, and assign the Grid.Row and Grid.Column properties (and even add the RowDefinitions if necessary).
<Grid>
<i:Interaction.Behaviors>
<my:GridItemsBehavior Items="{Binding TheArray}" />
</i:Interaction.Behaviors>
</Grid>
I wouldn't necessarily recommend the latter approach, because you lose a lot of the power of XAML by creating UI in code-behind.

change datatemplate resource runtime

I have created one datatemplate resouce in my xaml file
<navigation:Page.Resources>
<DataTemplate x:Key="PageFooter" >
<StackPanel Width="{Binding Path=UsablePageWidth, Mode=OneWay}" Height="Auto" x:Name="spFooter" HorizontalAlignment="Center">
<TextBlock x:Name="txtParameter" FontSize="16" Text="{Binding}"
FontWeight="Bold" Foreground="White"
HorizontalContentAlignment="Center"
Width="{Binding Path=UsablePageWidth, Mode=OneWay}"
Background="Black" Height="35" />
</StackPanel>
</DataTemplate>
</navigation:Page.Resources>
Now in my code behind i want to update this Textblock with my database value
How to do this? I am new in silverlight

Making a WPF TextBox be only as wide as the room it has but expand when the space expands?

Notice how the textbox expands to the right until it has enough horizontal space to fit the content? Well I'd like it to not expand and fit the text with the space it has in the window.
If the windows expands, then the Grid.Column it's in will expand, but the textbox itself should expand to fit. Simple enough?
Any suggestions? This is my first foray into WPF and so far it's been pretty sleek.
Edit: Here's my XAML markup:
<Window x:Class="GameLenseWpf.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="450" Width="350" MinHeight="450" MinWidth="350">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="0.15*" />
<RowDefinition />
</Grid.RowDefinitions>
<Image Grid.Row="0" Stretch="Fill" Source="Image/topBarBg.png" />
<StackPanel Orientation="Horizontal" Grid.Row="0">
<TextBlock Text="Platform"
Foreground="White"
FontFamily="Georgia"
FontSize="15"
Margin="10"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
<ComboBox x:Name="cmbPlatform"
Margin="10"
FontFamily="Georgia"
FontSize="15"
MinHeight="30"
MinWidth="140"
VerticalAlignment="Center"
VerticalContentAlignment="Center" SelectionChanged="cmbPlatform_SelectionChanged">
<ComboBoxItem>All Platforms</ComboBoxItem>
<ComboBoxItem>Playstation 3</ComboBoxItem>
<ComboBoxItem>XBox 360</ComboBoxItem>
<ComboBoxItem>Wii</ComboBoxItem>
<ComboBoxItem>PSP</ComboBoxItem>
<ComboBoxItem>DS</ComboBoxItem>
</ComboBox>
</StackPanel>
<Image x:Name="imgAbout" Grid.Row="0" Source="Image/about.png"
Height="16" HorizontalAlignment="Right"
VerticalAlignment="Center"
Margin="0 0 10 0" />
<ListBox Grid.Row="1" x:Name="lstGames" Background="#343434" Padding="5">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Height="120" Margin="0 10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="90"/>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
</Grid.RowDefinitions>
<Border BorderBrush="#202020" BorderThickness="5" CornerRadius="4" Panel.ZIndex="0">
<Image Grid.Row="0" Grid.Column="0" Source="{Binding ImageUrl}" Stretch="Fill"/>
</Border>
<StackPanel Grid.Row="0" Grid.Column="1" Margin="12 0 0 0">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Title:" FontFamily="Arial" Foreground="White"/>
<TextBlock Text="{Binding Title}" FontFamily="Arial" Foreground="White" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Release Date:" FontFamily="Arial" Foreground="White" />
<TextBlock Text="{Binding ReleaseDate}" FontFamily="Arial" Foreground="White" />
</StackPanel>
<TextBlock Text="Synopsis" FontFamily="Arial" Foreground="White" />
<TextBox Background="#454545" Text="{Binding Synopsis}" MinHeight="76" />
</StackPanel>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
To get a TextBox to wrap inside a ListBox you can make the following changes:
Set the content of the listbox equal to the width of the listbox using: HorizontalContentAlignment="Stretch".
Disable the horizontal scrollbar of the listbox to prevent listbox from getting the desired size of the controls and preventing the word wrap in your textbox.
Set TextWrapping="Wrap" in the TextBox
Here is the XAML:
<ListBox Grid.Row="1" x:Name="lstGames" Background="#343434" Padding="5"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
HorizontalContentAlignment="Stretch" >
</ListBox>
<TextBox Text="{Binding Synopsis}" MinHeight="76" TextWrapping="Wrap" />
I believe you need to set the Margin property of your textbox control. In the designer, you can see little circles around each textbox (and each control when you focus them, for that matter). Click the little circle on the right side of the textbox, to make that control grow marginally with the available space in the current layout control (by clicking the circle, the margin will be added into the XAML).
I don't know if in your image you've already adjusted the window size, but with that image it appears you'll also need to set the width for your textbox.
Does this help?

Encapsulating repeated layout in WPF

In a datatemplate that I am binding to a viewmodel I have a grid like so:
<Grid>
.
. <!--Row & Col Definitions...-->
.
<TextBlock Text="Some Label" Style="{DynamicResource TextBlockLabelStyle}" />
<TextBlock Grid.Column="1" Text="{Binding SomeValue, Mode=OneWay}"/>
<Border Style="{DynamicResource SeparatorStyle}" />
<TextBlock Grid.Row="1" Text="Some Label" Style="{DynamicResource TextBlockLabelStyle}" />
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding SomeValue, Mode=OneWay}"/>
<Border Grid.Row="1" Style="{DynamicResource SeparatorStyle}" />
<TextBlock Grid.Row="2" Text="Some Label" Style="{DynamicResource TextBlockLabelStyle}" />
<TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding SomeValue, Mode=OneWay}"/>
<Border Grid.Row="2" Style="{DynamicResource SeparatorStyle}" />
</Grid>
I thought that adding this repeated pattern (TextBlock for label, TextBlock for value, horizontal rule) was getting tedious and thought it would be best to encapsulate it into a UserControl something like 'GridRow' e.g.:
<UserControl x:Class="GridRow">
<TextBlock Text="{Binding LabelText}" Style="{DynamicResource TextBlockLabelStyle}" />
<TextBlock Grid.Column="1" Text="{Binding ValueText, Mode=OneWay}"/>
<Border Style="{DynamicResource SeparatorStyle}" />
</UserControl>
Then I could just go something like:
<Grid>
<GridRow LabelText="Some Label" ValueText="{Binding SomeValue}"/>
<GridRow Grid.Row="1" LabelText="Some Label2" ValueText="{Binding SomeValue2}"/>
<GridRow Grid.Row="2" LabelText="Some Label3" ValueText="{Binding SomeValue3}"/>
</Grid>
and have the user control bind to LabelText and ValueText properties, perhaps through template binding?
My question is how to do this, and if this is the right way of doing it, or if it is possible to use Styles or datatemplates to do this?
Unfortunately this is something that is not easy to achieve. A Grid layout looks at the Grid.Row and Grid.Column properties of its immediate children in order to create the required layout. Therefore, nesting your UI controls in another Grid will break the layout.
A few options, this blog post fixes the problem, but is complex:
http://www.scottlogic.co.uk/blog/colin/2010/11/using-a-grid-as-the-panel-for-an-itemscontrol/
There is a nice Auto-grid here, I have not used it, but it looks pretty good:
http://whydoidoit.com/2010/10/06/automatic-grid-layout-for-silverlight/
Regards,
Colin E.

WPF ListView with column names?

I have this ListView and I'm using a DataTemplate (as you can see) for items.
How can i add column names to the ListView with this ItemTemplate definition?
I cannot use that GridViewColumn definition, because this ListView uses lazy data loading, so when there are too many rows, it fetches them on demand. GridViewColumn somehow does not function with this lazy loading.
<ListView Grid.Row="3">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid x:Name="grid" Background="Transparent" MinWidth="580" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="220" />
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100" />
<ColumnDefinition Width="70"/>
<ColumnDefinition Width="80"/>
</Grid.ColumnDefinitions>
<TextBlock VerticalAlignment="Center" Grid.Column="0" Text="{Binding Path=Benutzer.Value.Code}"/>
<TextBlock VerticalAlignment="Center" Grid.Column="1" Text="{Binding Path=Nachname}"/>
<TextBlock VerticalAlignment="Center" Grid.Column="2" Text="{Binding Path=Vorname}"/>
<TextBlock VerticalAlignment="Center" Grid.Column="3">
<TextBlock.Text>
<Binding Path="GeburtDate" StringFormat="{}{0:d}"/>
</TextBlock.Text>
</TextBlock>
<Button VerticalAlignment="Center" Grid.Column="4" Style="{StaticResource StyleEditButton}" Content="Öffnen..." Tag="{Binding}" Click="OpenPersonButton_Click"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ListView>
I don't think there is any point in using a ListView if, as you say, you cannot use GridView (or any other view). I would exchange ListView for ItemsControl (or ListBox if you need a selectable item or any of the other things that ListBox offers).
If what you say is true, you could just use a stack panel to position a header row above your data rows:
<StackPanel>
<!-- Header -->
<StackPanel Orientation="Horizontal">
<TextBlock Width="220" TextAlignment="Center" Text="Code" />
<TextBlock Width="100" TextAlignment="Center" Text="Nachname" />
<TextBlock Width="100" TextAlignment="Center" Text="Vorname" />
<TextBlock Width="70" TextAlignment="Center" Text="GeburtDate" />
</StackPanel>
<!-- Data rows -->
<ItemsControl>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" MinWidth="580">
<TextBlock Width="220" TextAlignment="Center" Text="{Binding Path=Benutzer.Value.Code}" />
<TextBlock Width="100" TextAlignment="Center" Text="{Binding Path=Nachname}" />
<TextBlock Width="100" TextAlignment="Center" Text="{Binding Path=Vorname}" />
<TextBlock Width="70" TextAlignment="Center" Text="{Binding Path=GeburtDate, StringFormat={0:d}}" />
<Button VerticalAlignment="Center" Grid.Column="4" Style="{StaticResource StyleEditButton}" Content="Öffnen..." Tag="{Binding}" Click="OpenPersonButton_Click"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
Hope that helps. You might also like to read about virtualizing panels. That might provide the kind of laziness you're talking about.

Resources