GroupBox control in UWP? - xaml

Getting acquainted with UWP. I'm developing an App for simulating electric circuits. There is a classic visual control called Frame, later called GroupBox in WPF.
It seems this control is absent in UWP.
There is a control called HeaderedContentControl in UWP.Toolkit library, but doesn't look the same. And seems the background and border properties don't work..
currently my code is:
<controls:HeaderedContentControl Margin="5"
BorderBrush="Black" BorderThickness="1"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch">
<controls:HeaderedContentControl.Header>
<Border BorderBrush="Black" BorderThickness="1">
<Border.RenderTransform>
<TranslateTransform Y="-10"/>
</Border.RenderTransform>
<TextBlock Text="Resistor Value"/>
</Border>
</controls:HeaderedContentControl.Header>
<local:ComponentValueBox Unit="Ohm" HorizontalAlignment="Left"
Value="{x:Bind resistorValue, Mode=TwoWay}"
ValueChanged="changeR"/>
</controls:HeaderedContentControl>
And what I see (in the flyout) is:
Not quite like the GroupBox control..
What I would like to see is something like following:
What Should I do?

UWP is different from WPF. UWP is based on windows runtime, WPF is based on .NET Framework. They all use XAML to layout UI elments, but they have different XAML rendering engine. You could not think that MS dropped the old classic control. They're totally on the different platform. We call 'UWP' as Unversal Windows Platform. For now, you're not able to find such a 'GroupBox', but it's a new platform, you might be able to see such a control in the future. Anything is possible.
For your requirement, like #Muzib said, you entirely could make a custom control to meet your requirement. I used UserControl TextBlock Border ContentControl to make such a 'GroupBox' for your reference.
Please see my following code sample:
<UserControl
x:Class="AppGroupBox.GroupBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:AppGroupBox"
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>
<TextBlock x:Name="HeaderTitle" Text="Header" Margin="7 0 0 0" LayoutUpdated="HeaderTitle_LayoutUpdated"></TextBlock>
<Border BorderBrush="Black" x:Name="border" BorderThickness="0 2 0 0" Margin="100 10 3 3" CornerRadius="0 5 0 0"></Border>
<Border BorderBrush="Black" BorderThickness="2 0 2 2" Margin="3 10 3 3" CornerRadius="5">
<ContentControl x:Name="Content" Margin="10 10 10 10">
</ContentControl>
</Border>
</Grid>
public sealed partial class GroupBox : UserControl
{
public GroupBox()
{
this.InitializeComponent();
}
public string Header
{
get { return (string)GetValue(HeaderProperty); }
set { SetValue(HeaderProperty, value); }
}
// Using a DependencyProperty as the backing store for Header. This enables animation, styling, binding, etc...
public static readonly DependencyProperty HeaderProperty =
DependencyProperty.Register("Header", typeof(string), typeof(GroupBox), new PropertyMetadata("Your Header", HeaderPropertyChangedCallback));
public static void HeaderPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue != e.OldValue)
{
(d as GroupBox).HeaderTitle.Text = e.NewValue?.ToString();
//(d as GroupBox).border.Margin = new Thickness((d as GroupBox).HeaderTitle.ActualWidth, 10, 3, 3);
}
}
public object CustomContent
{
get { return (object)GetValue(CustomContentProperty); }
set { SetValue(CustomContentProperty, value); }
}
// Using a DependencyProperty as the backing store for Content. This enables animation, styling, binding, etc...
public static readonly DependencyProperty CustomContentProperty =
DependencyProperty.Register("CustomContent", typeof(object), typeof(GroupBox), new PropertyMetadata(null,PropertyChangedCallback));
public static void PropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue != e.OldValue)
{
(d as GroupBox).Content.Content = e.NewValue;
}
}
private void HeaderTitle_LayoutUpdated(object sender, object e)
{
border.Margin = new Thickness(HeaderTitle.ActualWidth+10,10,3,3);
}
}
<local:GroupBox Header="My GroupBox" Height="300" Width="500">
<local:GroupBox.CustomContent>
<StackPanel>
<RadioButton Content="r1"></RadioButton>
<TextBox></TextBox>
</StackPanel>
</local:GroupBox.CustomContent>
</local:GroupBox>

I don't think there's such controls in UWP. Most probably you have to make your own CustomControl to achieve something that looks exactly lik that in UWP.
But hey, you can achieve something like that with a 'customized' ListView. Look at this:
<ListView Header="I am a header" BorderThickness="1" BorderBrush="Red" Width="250" Height="200" SelectionMode="None">
<ListView.HeaderTemplate>
<DataTemplate>
<ListViewHeaderItem Content="{Binding}"/>
</DataTemplate>
</ListView.HeaderTemplate>
<RadioButton>Any Value</RadioButton>
<RadioButton>1% standard?</RadioButton>
<RadioButton>5% standard</RadioButton>
</ListView>
It produces this output:
Of course You can make these items more dense if you want so.

Related

PrepareContainerForItemOverride works different in Desktop than in Mobile UWP

I wanted to change the color of an item of ListView according the data value.
It would be easy doing:
<ListView.ItemContainerStyle>
<Style TargetType = "ListViewItem" >
< Setter Property="Background" Value="{Binding EventType, Converter={StaticResource EventTypeToBackColorConverter}}" />
</ListView.ItemContainerStyle>
But the thing is that UWP does not support binding in Setter Properties.
My second attempt was overriding PrepareContainerForItemOverride of the ListView:
public class EventListView : ListView
{
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
{
base.PrepareContainerForItemOverride(element, item);
var listViewItem = element as ListViewItem;
var ev = item as EventType;
if(ev.Warning)
listViewItem.Background = new SolidColorBrush(Color.Red);
}
}
The above code works fine running in a PC with Windows 10 and UWP. It colors in red some items according the underlying data. When I run the same app in Windows Mobile, at beginning it works fine, but when I scroll up and then I scroll down, returning to the original view that was ok at beginning, now other items are also in red color.
What I am missing?
I am not sure the reason, but the following code works for me:
public class EventListView : ListView
{
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
{
base.PrepareContainerForItemOverride(element, item);
var listViewItem = element as ListViewItem;
var ev = item as EventType;
if(ev.Warning)
listViewItem.Background = new SolidColorBrush(Color.Red);
else
listViewItem.Background = null;
}
}
I have added listViewItem.Background = null
This is because when there are a large number of Items, by default ListView has implement the function of data virtualization. It's not a good idea to disable this function since it can achieve a better performance.
But for your scenario, there is a much easier method to solve your problem. Since you're trying to modify the style of ListViewItem in the code behind, and we can't modify the existed one, we can set a new style of ListViewItem to ListView for example like this:
private void Button_Click(object sender, RoutedEventArgs e)
{
var dynamicStyle = new Style();
dynamicStyle.TargetType = typeof(ListViewItem);
dynamicStyle.Setters.Add(new Setter(BackgroundProperty, Colors.Red));
listView.ItemContainerStyle = dynamicStyle;
}
Only one problem is, if you are setting the Background property to all the ListViewItem, it makes no difference than binding data to the Background property of ListView or setting the Background to ListView like this:
listView.Background = new SolidColorBrush(Colors.Red);
So I just assume that you want to modify the root control in the DataTemplate for example like the Grid in the following xaml:
<ListView x:Name="listView" ItemsSource="{x:Bind collection}">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem" x:Name="myListItemStyle">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<TextBlock Text="{Binding Testtext}" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Then in this scenario, you can use data binding probably like this:
<DataTemplate>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="{Binding EventType, Converter={StaticResource EventTypeToBackColorConverter}}">
<TextBlock Text="{Binding Testtext}" />
</Grid>
</DataTemplate>
Any way, if you insist to change some property of all ListViewItem in the ListView, you can use the first method I provided.

Windows Phone 8.1 Toggling the visibility of a TextBlock in a DataTemplate

I'm building a Windows Phone 8.1 Hub Application. One of the hub section contains a ListView that displays a list of articles. I'd like to add a Textblock to this hubsection which displays a message when the articles failed to download. The XAML Code is below:
<HubSection
x:Uid="ArticlesSection"
Header="ARTICLES"
DataContext="{Binding Articles}"
HeaderTemplate="{ThemeResource HubSectionHeaderTemplate}">
<DataTemplate>
<Grid>
<ListView
AutomationProperties.AutomationId="ItemListViewSection3"
AutomationProperties.Name="Items In Group"
SelectionMode="None"
IsItemClickEnabled="True"
ItemsSource="{Binding}"
ItemTemplate="{StaticResource BannerBackgroundArticleTemplate}"
ItemClick="ItemView_ItemClick"
ContinuumNavigationTransitionInfo.ExitElementContainer="True">
</ListView>
<TextBlock
x:Name="NoArticlesTextBlock"
HorizontalAlignment="Center"
VerticalAlignment="center"
Style="{StaticResource HeaderTextBlockStyle}"
TextWrapping="WrapWholeWords"
TextAlignment="Center"/>
</Grid>
</DataTemplate>
</HubSection>
The problem I'm having is that I can't access the TextBlock from the C# code. Is there an easier way to do this?
The problem I'm having is that I can't access the TextBlock from the C# code.
Yes, since the TextBlock is defined inside a DataTemplate, the TextBlock won't be available until the DataTemplate has been applied. Thus, the x:Name attribute won't automatically generate a variable reference in the InitializeComponent method in your *.g.i.cs file. (Read up on XAML Namescopes for more information).
If you want to access it from your code-behind, there are two ways:
The first way is the simplest: you can get a reference to the TextBlock in the sender argument of the Loaded event handler for that TextBlock.
<TextBlock Loaded="NoArticlesTextBlock_Loaded" />
Then in your code-behind:
private TextBlock NoArticlesTextBlock;
private void NoArticlesTextBlock_Loaded(object sender, RoutedEventArgs e)
{
NoArticlesTextBlock = (TextBlock)sender;
}
The second way is to traverse the visual tree manually to locate the element with the required name. This is more suitable for dynamic layouts, or when you have a lot of controls you want to reference that doing the previous way would be too messy. You can achieve it like this:
<Page Loaded="Page_Loaded" ... />
Then in your code-behind:
static DependencyObject FindChildByName(DependencyObject from, string name)
{
int count = VisualTreeHelper.GetChildrenCount(from);
for (int i = 0; i < count; i++)
{
var child = VisualTreeHelper.GetChild(from, i);
if (child is FrameworkElement && ((FrameworkElement)child).Name == name)
return child;
var result = FindChildByName(child, name);
if (result != null)
return result;
}
return null;
}
private TextBlock NoArticlesTextBlock;
private void Page_Loaded(object sender, RoutedEventArgs e)
{
// Note: No need to start searching from the root (this), we can just start
// from the relevant HubSection or whatever. Make sure your TextBlock has
// x:Name="NoArticlesTextBlock" attribute in the XAML.
NoArticlesTextBlock = (TextBlock)FindChildByName(this, "NoArticlesTextBlock");
}
Jerry Nixon has a good page on his blog about this.

custom live tile update issue

I'm working on a weather application for windows phone. One of the features that I want to take advantage of is live tiles. I have a background agent that runs when the user pins a city to the start page.
After it's been pinned, it makes a calls out to the internet to get some weather data. All of this works just fine.
Now for the problem.
Depending on the weather data that's returned, I want to update the tiles that are pinned to the start screen.
I have a number of different .xaml files (rain, snow, sun, etc) that represent each tile.
My first thought was that I would:
expose 2 properties on each tile (CityState and Temp)
set those 2 properties after the tile is created.
save the tile off into IsolatedStorage as an image that I can then use to update the tile on the start screen.
Here is the code that I have to do that:
var ctl = new Snow();
//just some dummy data to test
ctl.CityState = "Test, NY";
ctl.Temp = 25;
ctl.Measure(new Size(173, 173));
ctl.Arrange(new Rect(0, 0, 173, 173));
var bmp = new WriteableBitmap(173, 173);
bmp.Render(ctl, null);
bmp.Invalidate();
var iss =IsolatedStorageFile.GetUserStoreForApplication();
var filename = "/Shared/ShellContent/tileTest.jpg";
using (var stm = iss.CreateFile(filename))
{
bmp.SaveJpeg(stm, 173, 173, 0, 80);
}
tile.BackgroundImage = new Uri("isostore:" + filename, UriKind.Absolute);
var tileToUpdate = ShellTile.ActiveTiles.FirstOrDefault(r => r.NavigationUri == uri);
tileToUpdate.Update(tile);
So, when this runs, it creates a new tile from the XAML file and updates the start screen but the Temp and CityState properties
are not reflected on the new Tile. In the xaml I have 2 textblocks that are bound to the properties in the codebehind. I've also
implemented INotifyPropertyChanged.
Here is the XAML
<UserControl
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"
mc:Ignorable="d"
x:Name="Window"
x:Class="ezweather.services.tiles.Snow"
d:DesignWidth="480" d:DesignHeight="800" Width="173" Height="173" >
<Canvas x:Name="Layer_1" Width="173" Height="173" Canvas.Left="0" Canvas.Top="0" >
<Rectangle x:Name="Rectangle" Width="173" Height="173" Canvas.Left="0" Canvas.Top="-1.52588e-005" Stretch="Fill" Fill="#FF3F6A8D"/>
<TextBlock x:Name="cityState" TextAlignment="Left" FontFamily="Segoe UI Semibold" FontWeight="Bold" FontSize="15" Width="Auto" Height="Auto" Canvas.Left="0" Canvas.Top="0">
<TextBlock.RenderTransform>
<TransformGroup>
<MatrixTransform Matrix="1.33333,0,0,1.33333,11,139.5"/>
</TransformGroup>
</TextBlock.RenderTransform>
<Run Text="{Binding ElementName=Window, Path=CityState}" Foreground="#FFFFFFFF"/>
</TextBlock>
<TextBlock x:Name="temp" TextAlignment="Right" FontFamily="Segoe UI Light" FontSize="44" Width="Auto" Height="Auto" Canvas.Left="0" Canvas.Top="0">
<TextBlock.RenderTransform>
<TransformGroup>
<MatrixTransform Matrix="1.33333,0,0,1.33333,87.57,42.9333"/>
</TransformGroup>
</TextBlock.RenderTransform>
<Run Text="{Binding ElementName=Window, Path=Temp}" Foreground="#FFFFFFFF"/>
</TextBlock>
</Canvas>
</UserControl>
and here is the codebehind
public partial class Snow : UserControl, INotifyPropertyChanged
{
public Snow()
{
// Required to initialize variables
InitializeComponent();
}
private string _cityState;
private int _temp;
public string CityState
{
get { return _cityState; }
set
{
_cityState = value;
RaisePropertyChanged("CityState");
}
}
public int Temp
{
get { return _temp; }
set
{
_temp = value;
RaisePropertyChanged("Temp");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string property)
{
if(PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
When this code runs, it instantiates the correct xaml file and saves it to disk.
It then updates the tile on the start screen but the CityState and Temp data does not show up.
I don't know why the CityState and Temp data isn't being written out with the image.
What am I missing?
The primary issue I see here, is you're attempting to render the image, before the control is actually loaded.
Try handle the rendering in the Control.Loaded event.

Code behind works but MVVM doesnt

I have been messing with something that works in the code behind but when I try and bind to a MVVM , nothing displays. First I will show the code behind, then MVVM ( same xaml ). I want to use MVVM and not code behind.
Code Behind (works):
var loadOp = ctx.Load<GateBlox.Web.Models.Structure>(ctx.GetStructuresQuery());
loadOp.Completed += (s, e) => { _treeView.ItemsSource = loadOp.Entities.Where(struc => !struc.StructureParentFK.HasValue); };
XAML
<Grid x:Name="LayoutRoot">
<sdk:TreeView x:Name='_treeView' DataContext='{StaticResource ViewModel}'>
<sdk:TreeView.ItemTemplate>
<sdk:HierarchicalDataTemplate ItemsSource='{Binding Children}'>
<TextBlock Text='{Binding StructureName}' />
</sdk:HierarchicalDataTemplate>
</sdk:TreeView.ItemTemplate>
</sdk:TreeView>
</Grid>
MVVM (doesnt bind)
private LoadOperation<Structure> _loadStructures;
private StructureContext _structureContext;
private IEnumerable<Structure> _structures;
public IEnumerable<Structure> Structures
{
get { return this._structures; }
set { this._structures = value; RaisePropertyChanged("Structures"); }
}
public StructuresViewModel()
{
if (!DesignerProperties.IsInDesignTool)
{
_structureContext = new StructureContext();
_loadStructures = _structureContext.Load(_structureContext.GetStructuresQuery().Where (p=> ! p.StructureParentFK.HasValue));
_loadStructures.Completed += new EventHandler(_loadStructures_Completed);
}
}
void _loadStructures_Completed(object sender, EventArgs e)
{
this.Structures = _loadStructures.Entities;
}
Have your checked that you are not getting a binding expression error in the output? You are binding the items source of the data template to a property named Children, but your view model exposes a data source named Structures.
Also, in your working example, you are setting the ItemsSource of the TreeView, but in your MVVM XAML you are setting the ItemsSource of your data template. Is there an inconsistency between what ItemsSource you need to set/bind to?
You might also consider using a collection data source that implements the INotifyCollectionChanged interface (ObservableCollection or expose the binding source as a ICollectionView that uses a PagedCollectionView).
I recommend you take a look at this information about data binding in MVVM, as it provides excellent guidance on setting up data sources in your view models.
You are not setting the ItemsSource for your TreeView. I think your xaml should look something like this:
<Grid x:Name="LayoutRoot">
<sdk:TreeView x:Name='_treeView' DataContext='{StaticResource ViewModel}'
ItemsSource="{Binding Structures}">
<sdk:TreeView.ItemTemplate>
<sdk:HierarchicalDataTemplate ItemsSource='{Binding Children}'>
<TextBlock Text='{Binding StructureName}' />
</sdk:HierarchicalDataTemplate>
</sdk:TreeView.ItemTemplate>
</sdk:TreeView>
</Grid>
Hope this helps :)
I almost have it working now. I took a different approach and went with a HeirarchicalDataTemplate. At the moment the data is showing but not correctly: The child1 record is shwoing up as a parent as well.
Parent1(level1)
Parent2(level1)
Child1(level2)
Child1(level1)
<navigation:Page x:Class="GateBlox.Views.Structure"
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"
mc:Ignorable="d"
xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
d:DesignWidth="640"
d:DesignHeight="480"
Title="Structure Page"
xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
xmlns:viewmodel="clr-namespace:GateBlox.ViewModels">
<UserControl.Resources>
<viewmodel:StructuresViewModel x:Key='ViewModel'>
</viewmodel:StructuresViewModel>
</UserControl.Resources>
<Grid x:Name="LayoutRoot"
DataContext='{StaticResource ViewModel}'>
<Grid.Resources>
<sdk:HierarchicalDataTemplate x:Key="ChildTemplate"
ItemsSource="{Binding Path=Parent}">
<TextBlock FontStyle="Italic"
Text="{Binding Path=StructureName}" />
</sdk:HierarchicalDataTemplate>
<sdk:HierarchicalDataTemplate x:Key="NameTemplate"
ItemsSource="{Binding Path=Children}"
ItemTemplate="{StaticResource ChildTemplate}">
<TextBlock Text="{Binding Path=StructureName}"
FontWeight="Bold" />
</sdk:HierarchicalDataTemplate>
</Grid.Resources>
<sdk:TreeView x:Name='treeView'
Width='400'
Height='300'
ItemsSource='{Binding Structures}'
ItemTemplate='{StaticResource NameTemplate}'>
</sdk:TreeView>
</Grid>
using System;
using System.Collections.ObjectModel;
using GateBlox.Web.Models;
using System.ServiceModel.DomainServices.Client;
using GateBlox.Web.Services;
using GateBlox.Helpers;
using System.ComponentModel;
using System.Collections.Generic;
namespace GateBlox.ViewModels
{
public class StructuresViewModel : ViewModelBase
{
private LoadOperation<Structure> _loadStructures;
private StructureContext _structureContext;
private ObservableCollection<Structure> _structures;
public ObservableCollection<Structure> Structures
{
get { return this._structures; }
set { this._structures = value; RaisePropertyChanged("Structures"); }
}
public StructuresViewModel()
{
if (!DesignerProperties.IsInDesignTool)
{
_structureContext = new StructureContext();
_loadStructures = _structureContext.Load(_structureContext.GetStructuresQuery());
_loadStructures.Completed += new EventHandler(_loadStructures_Completed);
}
}
void _loadStructures_Completed(object sender, EventArgs e)
{
this.Structures = IEnumerableConverter.ToObservableCollection(_loadStructures.Entities);
}
}
}

How to attach EventTrigger in code behind in Silverlight 4

My question is the following:
I have a grid and I attached the SelectedIndexChanged event the following way in the xaml file:
"<cc:DetailViewGrid AutoGenerateColumns="False" HorizontalAlignment="Stretch" Margin="0,0,0,0" Name="dgAcitivityList" VerticalAlignment="Stretch" ItemsSource="{Binding EntityList}" SelectionMode="Single" IsReadOnly="False">
<interactivity:Interaction.Triggers>
<interactivity:EventTrigger EventName="SelectionChanged">
<interactivity:InvokeCommandAction Command="{Binding SelectedItemChangeCommand}" CommandParameter="{Binding SelectedItem, ElementName=dgAcitivityList}"/>
</interactivity:EventTrigger>
</interactivity:Interaction.Triggers>"
But I want to attach this event in code behind. I ctreated an own grid that is inherited from windows grid, and I put this code to own control.
public override void OnApplyTemplate()
{
//base.OnApplyTemplate();
System.Windows.Interactivity.EventTrigger selectedItemChangedTrigger = new System.Windows.Interactivity.EventTrigger("SelectionChanged");
System.Windows.Interactivity.InvokeCommandAction action = new System.Windows.Interactivity.InvokeCommandAction();
action.CommandName = "{Binding SelectedItemChangeCommand}";
action.CommandParameter = string.Format("{{Binding SelectedItem, ElementName={0}}}", this.Name);
selectedItemChangedTrigger.Actions.Add(action);
System.Windows.Interactivity.Interaction.GetTriggers(this).Add(selectedItemChangedTrigger);
base.OnApplyTemplate();
}
Is this solution proper? It's not working but I'm not sure that I should put this code in the OnApplyTemplate() method.