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);
}
}
}
Related
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.
I'm trying to use the XamlReader to parse a XAML File on during runtime. Unfortunately I get a XamlParseException when the XamlReader tries to read the Relative Attributes like RelativePanel.Below.
This is the Code to load the xaml file:
using System;
using System.IO;
using Windows.Storage;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Markup;
namespace TestProject.UWP.Views
{
public sealed partial class LoginPage : Page
{
public LoginPage()
{
this.InitializeComponent();
Loaded += OnLoaded;
}
private async void OnLoaded(object sender, RoutedEventArgs routedEventArgs)
{
var folder = Windows.ApplicationModel.Package.Current.InstalledLocation;
folder = await folder.GetFolderAsync("TestData");
var file = await folder.GetFileAsync("LoginControl.xaml");
var xaml = await FileIO.ReadTextAsync(file);
var content = (UserControl)XamlReader.Load(xaml);
this.Content = content;
}
}
}
And this the xaml file i try to read from the local content
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:TestProject.UWP.Views"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="800"
d:DesignWidth="600">
<RelativePanel Background="LightGray">
<Border x:Name="logoBorder" BorderBrush="White" BorderThickness="0,0,0,1" Margin="30,30,30,10" Width="200" Height="60" Padding="0,0,0,5" RelativePanel.AlignLeftWithPanel="True" RelativePanel.AlignRightWithPanel="True" RelativePanel.AlignTopWithPanel="True" >
<Image Stretch="Uniform" Source="ms-appx:///Assets/Images/logo.png" Width="200" />
</Border>
<Image x:Name="userIcon" Source="ms-appx:///Assets/Images/usericon.png" Margin="30,10" RelativePanel.AlignHorizontalCenterWithPanel="True" RelativePanel.AlignRightWith="logoBorder" Width="100" Height="100"/>
</RelativePanel>
</UserControl>
When i try to parse the xaml i get the following Exception:
"WinRT information: RelativePanel error: Value must be of type UIElement."
As soon as i remove the attribute RelativePanel.AlignRightWith="logoBorder" from the second image everythings work fine.
Does someone has a idea to workaround this problem?
Edit:
Before you ask. The xaml should later be loaded from a server that's why i don't just instantiate a instance of the usercontrol in code.
Cheers
Kornelis
Replace the element name in
RelativePanel.AlignRightWith="logoBorder"
by an ElementName binding:
RelativePanel.AlignRightWith="{Binding ElementName=logoBorder}"
I have a question concerning data binding with tabs.
I have the following xaml code:
<Window x:Class="SuperAtomsController.GUI.windowAnalog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="windowAnalog" Height="900" Width="1400"
DataContext="{Binding RelativeSource={RelativeSource self}}">
<Grid>
<TabControl Height="459" HorizontalAlignment="Left" Margin="188,278,0,0" Name="tabControl1" ItemsSource="{Binding Path=model.sequences}" VerticalAlignment="Top" Width="883">
</TabControl>
</Grid>
</Window>
And the code behind:
public partial class windowAnalog : Window
{
public Data model;
public windowAnalog(Data model)
{
this.model = model;
InitializeComponent();
}
}
But with this nothing appears in the tabcontrol (model.sequences is of the type ObservableCollection<>). If remove the itemssource in xaml and add the following in the c# code tabControl1.ItemsSource = model.sequences; after the InitializeComponent(); it works fine. What am I missing?
Clearly your binding isn't resolving, check your debug output window for helpful diagnostic messages.
I can't recall but I think model may need to be a property instead of a field for WPF property path to work. Otherwise maybe it was a problem with the DataContext. You coudl try doing this.DataContext = this to your constructor before InitializeComponent() instead of your DataContext xaml.
I have a form with some validations set in entity metadata class. and then binding entity instance to UI by VM. Something as below:
Xaml like:
<Grid x:Name="LayoutRoot">
<StackPanel VerticalAlignment="Top">
<input:ValidationSummary />
</StackPanel>
<TextBox Text="{Binding Name, Mode=TwoWay}" />
<ComboBox x:Name="xTest" ItemsSource="{Binding MyList}"
SelectedItem="{Binding MyItem,Mode=TwoWay,
DisplayMemberPath="MyName"
ValidatesOnDataErrors=True,
ValidatesOnNotifyDataErrors=True,
ValidatesOnExceptions=True,
NotifyOnValidationError=True,UpdateSourceTrigger=Explicit}" />
</Grid>
Code-behind like:
public MyForm()
{
InitializeComponent();
this.xTest.BindingValidationError +=new EventHandler<ValidationErrorEventArgs>((s,e)=>{
BindingExpression be = this.xTest.GetBindingExpression(ComboBox.SelectedItemProperty);
be.UpdateSource();
if (e.Action == ValidationErrorEventAction.Added)
((ComboBox)s).Foreground = new SolidColorBrush(Colors.Red);
});
}
Metadata like:
[Required]
public string Name { get; set; }
[RequiredAttribute]
public int MyItemID { get; set; }
But when running the app, I got nothing display in valudationSummary.
For CombBox, even there is error, looks like BindingValidationError event is never fired.
How to resolve it?
Why are you using an Explicit UpdateSourceTrigger?
Silverlight validation happens inside the binding framework, when the binding is updating the source object. The way you have this, there won't be a binding validation error because you never tell the binding to update the source object. Well, actually you do, but it happens inside the validation error event handler. You've written chicken-and-egg code.
Remove your UpdateSourceTrigger on your binding or set it to Default.
Remove the explicit call to BindingExpression.UpdateSource.
Remove setting the ComboBox foreground to red - you are using NotifyOnValidationError=True, which eliminates any need to manually color the control.
Remove the DisplayMemberPath from the binding
So your XAML:
<Grid x:Name="LayoutRoot">
<StackPanel VerticalAlignment="Top">
<input:ValidationSummary />
<ComboBox x:Name="xTest" ItemsSource="{Binding MyList}"
SelectedItem="{Binding MyItem,
Mode=TwoWay,
ValidatesOnDataErrors=True,
ValidatesOnNotifyDataErrors=True,
ValidatesOnExceptions=True,
NotifyOnValidationError=True}" />
</StackPanel>
</Grid>
And your code:
public MyForm()
{
InitializeComponent();
// you don't need anything here to have the validations work
}
I have a question related to this one: I'm trying to attach an event to my StackPanel but it doesn't appear to connect when using the XamlReader. I can't get the ChildItem_Load method to get called. Does anyone know of a way to do this?
Other than this event the code works fine.
this._listBox.ItemTemplate = (DataTemplate) XamlReader.Load(
#"<DataTemplate xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"">
<Border>
<StackPanel Loaded=""ChildItem_Loaded"">
<TextBlock Text=""{Binding " + this._displayMemberPath + #"}"" />
</StackPanel>
</Border>
</DataTemplate>"
Ok, I figured out a bit of a 'hack' solution, but it works.
Since it looks like the XamlReader doesn't have any knowledge of the local namespace when creating the DataTemplate I extended the StackPanel and "baked-in" the Load event. It's not exactly ideal, but it works:
this._listBox.ItemTemplate = (DataTemplate) XamlReader.Load(
#"<DataTemplate xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
xmlns:foo=""clr-namespace:Foo;assembly=Foo"">
<Border>
<foo:ExtendedStackPanel>
<TextBlock Text=""{Binding " + this._displayMemberPath + #"}"" />
</foo:ExtendedStackPanel>
</Border>
</DataTemplate>"
);
And the extended class:
public class ExtendedStackPanel : StackPanel
{
public ExtendedStackPanel() : base()
{
this.Loaded += new RoutedEventHandler(this.ChildItem_Loaded);
}
private void ChildItem_Loaded(object sender, RoutedEventArgs e)
{
// Logic here...
}
}