Disclaimer: I'm new to Xamarin.Forms and Xaml in general
I'm trying to figure out how DataBinding works within templates in Xamarin.Forms.
Xaml:
<ContentPage.Resources>
<ResourceDictionary>
<ControlTemplate x:Key="GridItem">
<Grid HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" Padding="0" ColumnSpacing="0" RowSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<ContentView Grid.Row="0" Grid.Column="0" BackgroundColor="Red" VerticalOptions="FillAndExpand" Padding="15">
<Image Source="{TemplateBinding ImageSource}" BackgroundColor="Red" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand" />
</ContentView>
<Label Grid.Row="1" Grid.Column="0" Text="{TemplateBinding LabelText}" FontSize="Large" TextColor="White" BackgroundColor="#8B0000" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" />
</Grid>
</ControlTemplate>
</ResourceDictionary>
</ContentPage.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="1*" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<ContentView x:Name="randomButton1" ControlTemplate="{StaticResource GridItem}" Grid.Row="0" Grid.Column="0" />
<ContentView x:Name="randomButton2" ControlTemplate="{StaticResource GridItem}" Grid.Row="0" Grid.Column="1" />
</Grid>
C# Code behind
using Xamarin.Forms;
namespace TestApp.XForms
{
public partial class Page1 : ContentPage
{
private readonly MainButtonViewModel[] buttons;
public Page1()
{
InitializeComponent();
randomButton1.BindingContext = new MainButtonViewModel("some text1", "someimage1.png");
randomButton2.BindingContext = new MainButtonViewModel("some text2", "someimage2.png");
}
}
public class MainButtonViewModel : BindableObject
{
private string labelText;
public string LabelText
{
get { return labelText; }
set
{
labelText = value;
OnPropertyChanged();
}
}
private string imageSource;
public string ImageSource
{
get { return imageSource; }
set
{
imageSource = value;
OnPropertyChanged();
}
}
public MainButtonViewModel()
{
}
public MainButtonViewModel(string text, string imageLocation)
{
LabelText = text;
ImageSource = imageLocation;
}
}
}
So my templates seem to work. I see 2 red blocks. But none of the bindings seem to have worked. Everything is empty:
How can I get the data binding to work?
Your view model doesn't support INotifyPropertyChanged. You could use Xamarin.Forms class:
public class SomeViewModel : BindableObject
And then you could notify UI, that some property has been changed:
private string _someStringProperty;
public string SomeStringProperty
{
get { return _someStringProperty; }
set
{
_someStringProperty = value;
OnPropertyChanged();
}
}
Also will be helpful Binding from ControlTemplate.
When you use ControlTemplate it requires TemplateBinding for BindingContext, otherwise it won't have any BindingContext itself. You can use DataTemplate without setting BindingContext, it inherits parent's BindingContext itself.
i.e:
<ControlTemplate x:Key="yourTemplate" BindingContext="{TemplateBinding BindingContext}">
<Grid>
<!--Your Template-->
</Grid>
</ControlTemplate>
<DataTemplate x:Key="yourTemplate">
<Grid>
<!--Your Template-->
</Grid>
</DataTemplate>
Related
I need to bind the property SelectedItem of a datagrid called dg_item in the class ItemList to a textbox in the page ItemViewMenu.
I have declared the DataContext in in the ItemViewMenu class as followed:
ItemViewMenu Menu = this;
ItemList List = PageResources.ItemListInstance;
this.DataContext = new { Menu, List };
This is the PageResources.ItemListInstance:
class PageResources
{
private static ItemList _itemListInstance;
public static ItemList ItemListInstance
{
get
{
if (_itemListInstance == null)
_itemListInstance = new ItemList();
return _itemListInstance;
}
}
}
The dg_item datagrid contains a list from the type Item which contains
the following property I need to access:
public class Item
{
private string _manufacturerName;
public string ManufacturerName
{
get
{
return _manufacturerName;
}
set
{
if (value != null)
{
_manufacturerName= value;
}
}
}
...
}
And I'm trying to access the Property in the "ItemViewMenu.xaml" like that:
<Grid DataContext="{Binding Source={StaticResource List.dg_item}, Path=SelectedItem}" Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.3*" />
<ColumnDefinition Width="0.7*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock x:Name="txt_manufacturer" Grid.Row="0" Grid.Column="0" HorizontalAlignment="Left" Text="Manufacturer: " Margin="5,2" VerticalAlignment="Center" FontSize="13"/>
<TextBox x:Name="tb_manufacturer" Text="{Binding ManufacturerName}" Grid.Row="0" Grid.Column="1" Margin="5,2" FontSize="13" IsReadOnly="True" VerticalContentAlignment="Center" MaxHeight="50" />
...
</Grid>
But this doesn't work. So how do I access the property ManufacturerName?
Thanks in advance for any support! I am trying to create a custom tab item to act as a canvas for dynamically creating UI elements. Here is an image to give an idea as to what I would like in this custom control:
I need to be able to generate the Tab Items dynamically to the TabControl in a parent form - the problem is that my code seems to do nothing to the TabItem - it's just always blank and it doesn't complain about my code. What's missing? Thanks for any help!
My WPF user control tabitem Code:
<UserControl x:Class="Configuration_Manager.SearchTab"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Configuration_Manager"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<TabItem Header="Search Configuration Name">
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<GroupBox Header="Git Repo Credentials:">
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="3*" />
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Content="Server Address:" />
<TextBox Grid.Column="1" Margin="2" />
</Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="3*" />
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Content="Username:" />
<TextBox Grid.Column="1" Margin="2" />
</Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="3*" />
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Content="Password:" />
<TextBox Grid.Column="1" Margin="2" />
</Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<CheckBox x:Name="CheckBoxStoreCredentials" Content="Store Credentials" Grid.Column="0" IsChecked="False" VerticalAlignment="Center"/>
<Button x:Name="ButtonDownloadConfiguration" Content="Download Configuration" Grid.Column="2" Margin="5" />
</Grid>
</StackPanel>
</GroupBox>
</Grid>
</StackPanel>
</TabItem>
</UserControl>
The Designer:
In WPF if you want to dynamically create controls you always have to use templates. TabControl is an ItemsControl. TabItem elements (the tabs) are automatically generated for each item inside the ItemsControl.ItemsSource collection. The visual of this TabItem can be designed by using styles and templates.
Use the TabControl.ContentTemplate property to specify the DataTemplate for the content of each tab
First you have to create the data model that should be displayed inside a TabItem.
TabData.cs:
public class TabData : INotifyPropertyChanged
{
public TabData(string header)
{
this.Header = header;
}
public string Header { get; set; }
private string serverAddress;
public string ServerAddress
{
get => this.serverAddress;
set
{
if (value == this.serverAddress) return;
this.serverAddress = value;
OnPropertyChanged();
}
}
private string username;
public string Username
{
get => this.username;
set
{
if (value == this.username) return;
this.username = value;
OnPropertyChanged();
}
}
private string password;
public string Password
{
get => this.password;
set
{
if (value == this.password) return;
this.password = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Then create a view model that handles the tab data and exposes the items source for the TabControl.
The view model is the DataContext of the TabControl.
ViewModel.cs
public class ViewModel: INotifyPropertyChanged
{
public ViewModel()
{
this.TabDatas = new ObservableCollection<TabData>()
{
new TabData("First Tab"),
new TabData("Second Tab"),
new TabData("Third Tab")
};
}
// Adding a new TabData item to the TabDatas collection
// will dynamically create a new TabItem inside the TabControl
public void AddNewTab()
{
this.TabDatas.Add(new TabData("Fourth Tab"));
}
public ObservableCollection<TabData> TabDatas { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
AddNewTab can be invoked from a ICommand (e.g. on button clicked) or some event (e.g. new data available).
MainWindow.xaml:
<Window>
<Window.DataContext>
<ViewModel />
</Window.DataContext>
<Grid>
<!-- Use DisplayMemberPath property to set the source property for the tab header -->
<TabControl x:Name="TabControl"
ItemsSource="{Binding TabDatas}"
DisplayMemberPath="Header" >
<!-- Use a DataTemplate to design the visual appearance of the TabItem content -->
<TabControl.ContentTemplate>
<DataTemplate DataType="TabData">
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<GroupBox Header="Git Repo Credentials:">
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="3*" />
</Grid.ColumnDefinitions>
<Label Grid.Column="0"
Content="Server Address:" />
<TextBox Grid.Column="1"
Margin="2"
Text="{Binding ServerAddress}" />
</Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="3*" />
</Grid.ColumnDefinitions>
<Label Grid.Column="0"
Content="Username:" />
<TextBox Grid.Column="1"
Margin="2"
Text="{Binding Username}"/>
</Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="3*" />
</Grid.ColumnDefinitions>
<Label Grid.Column="0"
Content="Password:" />
<TextBox Grid.Column="1"
Margin="2"
Text="{Binding Password}"/>
</Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<CheckBox x:Name="CheckBoxStoreCredentials"
Content="Store Credentials"
Grid.Column="0"
IsChecked="False"
VerticalAlignment="Center" />
<Button x:Name="ButtonDownloadConfiguration"
Content="Download Configuration"
Grid.Column="2"
Margin="5" />
</Grid>
</StackPanel>
</GroupBox>
</Grid>
</StackPanel>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
</Window>
I have a WPF application and just embarked in learning MVVM pattern.
My goal is that in my application the main window has a button. When this button is clicked, another window (or user control) will appear on top of the main window.
This is the code of MainWindow.xaml
<Window x:Class="SmartPole1080.View.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:utilities="clr-namespace:SoltaLabs.Avalon.Core.Utilities;assembly=SoltaLabs.Avalon.Core"
xmlns:userControls="clr-namespace:SoltaLabs.Avalon.View.Core.UserControls;assembly=SoltaLabs.Avalon.View.Core"
xmlns:controls="clr-namespace:WpfKb.Controls;assembly=SmartPole.WpfKb"
xmlns:wpf="clr-namespace:WebEye.Controls.Wpf;assembly=WebEye.Controls.Wpf.WebCameraControl"
xmlns:view="clr-namespace:SmartPole.View;assembly=SmartPole.View"
xmlns:view1="clr-namespace:SmartPole1080.View"
xmlns:behaviors="clr-namespace:SoltaLabs.Avalon.Core.Behaviors;assembly=SoltaLabs.Avalon.Core"
Title="Smart Pole"
WindowStartupLocation="CenterScreen"
Name="mainWindow"
behaviors:IdleBehavior.IsAutoReset="True" WindowState="Maximized" WindowStyle="None">
<Canvas Background="DarkGray">
<!--Main Grid-->
<Grid Width="1080" Height="1920" Background="White" Name="MainGrid"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<StackPanel Background="Black">
<Grid Background="#253341">
<Grid.RowDefinitions>
<RowDefinition Height="5"/>
<RowDefinition Height="*"/>
<RowDefinition Height="5"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="5"/>
<ColumnDefinition Width="264"/>
</Grid.ColumnDefinitions>
<Grid Grid.Row="1" Grid.Column="1">
<Button Tag="{StaticResource EmergencyImg}" Name="EmergencyButton"
Command="{Binding ShowEmergencyPanel}">
<Image Source="{StaticResource EmergencyImg}" />
</Button>
</Grid>
<!--Emergency Window Dialog-->
<Grid Name="EmergencyPanel">
<view1:EmergencyInfo x:Name="EmergencyInfoPanel"/>
</Grid>
</Grid>
</StackPanel>
</Grid>
<!--Main Grid-->
</Canvas>
This is the other window (user control - EmergencyInfo.xaml)
<UserControl x:Class="SmartPole1080.View.EmergencyInfo"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:SmartPole1080.View"
mc:Ignorable="d"
d:DesignHeight="1920" d:DesignWidth="1050"
x:Name="EmergencyInfoPanel">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Border Grid.Row="0" BorderBrush="White" Background="White">
<Button Background="White" BorderThickness="0" FontWeight="Bold" Foreground="Red"
HorizontalAlignment="Right" FontSize="25" Margin="0,0,15,0"
Command="{Binding HideEmergencyPanel}">
close X
</Button>
</Border>
<Image Grid.Row="1" Source="{StaticResource EdenParkInfoImg}" HorizontalAlignment="Left" />
</Grid>
I want to implement this behavior using an MVVM pattern. I have set the binding ShowEmergencyPanel in button EmergencyButton to show EmergencyInfo when this button is click.
Any help is greatly appreciated.
Why dont you make navigation, something like this. Make section for conetent that will be injected, and whatever type of object you are expecting put it in Windows.Resources in DataTemplate.
In main windo xaml
<Window.DataContext>
<local:MainWindowViewModel />
</Window.DataContext>
<Window.Resources>
<DataTemplate DataType="{x:Type home:HomeViewModel}">
<home:HomeView />
</DataTemplate>
<DataTemplate DataType="{x:Type other:OtherViewModel}">
<other:OtherView />
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid x:Name="Navigation">
<StackPanel Orientation="Horizontal">
<Button x:Name="HomeView"
Content="Home"
Margin="5"
Command="{Binding NavigationCommand}"
CommandParameter="home" />
<Button x:Name="Menu"
Content="OtherView"
Margin="5"
Command="{Binding NavigationCommand}"
CommandParameter="Other" />
</StackPanel>
</Grid>
<Grid x:Name="MainContent"
Grid.Row="1">
<ContentControl Content="{Binding CurrentViewModel}" />
</Grid>
</Grid>
MainWindowViewModel can look something like this.
public class MainWindowViewModel : INotifyPropertyChanged
{
private OtherViewModel otherVM;
private HomeViewModel homeVM;
public DelegateCommand<string> NavigationCommand { get; private set; }
public MainWindowViewModel()
{
otherVM = new OtherViewModel();
homeVM = new HomeViewModel();
// Setting default: homeViewModela.
CurrentViewModel = homeVM;
NavigationCommand = new DelegateCommand<string>(OnNavigate);
}
private void OnNavigate(string navPath)
{
switch (navPath)
{
case "other":
CurrentViewModel = otherVM;
break;
case "home":
CurrentViewModel = homeVM;
break;
}
}
private object _currentViewModel;
public object CurrentViewModel
{
get { return _currentViewModel; }
set
{
if (_currentViewModel != value)
{
_currentViewModel = value;
OnPropertyChanged();
}
}
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged = delegate { };
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged(this, new propertyChangedEventArgs(propertyName));
}
#endregion
}
Where DelegateCommand you can make yours,check how to make RelayCommand(and generic one) or use PRISM that have it's own DelegateCommand. But if you want to use PRISM, it allready has navigation to regions, that can solve many problems. Check videos from Brian Lagunas.
EDIT:
This is to show/hide grid. In your mainWindow where you set that EmergencyInfo u can show/hide that grid this way.
in your MainViewViewModel make a bool property IsVisible
private bool _isVisible;
public bool IsVisible
{
get { return _isVisible; }
set
{
_isVisible = value;
OnPropertyChanged();
}
}
in your MainView.Resources set key to BooleanToVisibilityConverter
something like:
<BooleanToVisibilityConverter x:Key="Show"/>
and your grid that you want to show/hide set visibility:
<Grid x:Name="SomeGridName"
Grid.Row="1"
Grid.Colum="1"
Visibility="{Binding IsVisible,Converter={StaticResource Show}}">
And finally set that IsVisible property to some ToggleButton, just to switch between true/false
<ToggleButton IsChecked="{Binding IsVisible}"
Content="ShowGrid" />
This way, you show/hide that grid part based on IsVisible, and you control that visibility onClick. Hope that helps.
Inside your Window xaml:
<ContentPresenter Content="{Binding Control}"></ContentPresenter>
In this way, your ViewModel must contain a Control property.
Add your ViewModel to DataContext of Window.
(For example in window constructor, this.Datacontext = new ViewModel();)
Or another way with interfaces:
public interface IWindowView
{
IUserControlKeeper ViewModel { get; set; }
}
public interface IUserControlKeeper
{
UserControl Control { get; set; }
}
public partial class YourWindow : IViewWindow
{
public YourWindow()
{
InitializeComponent();
}
public IUserControlKeeper ViewModel
{
get
{
return (IUserControlKeeper)DataContext;
}
set
{
DataContext = value;
}
}
}
(In this way, initialize your window where you want to use. Service?)
private IViewWindow _window;
private IViewWindow Window //or public...
{
get{
if(_window==null)
{
_window = new YourWindow();
_window.ViewModel = new YourViewModel();
}
return _window;
}
}
Open your window with one of your UserControls:
void ShowWindowWithControl(object ControlView, INotifyPropertyChanged ControlViewModel, bool ShowAsDialog)
{
if(ControlView is UserControl)
{ //with interface: Window.ViewModel.Control = ...
(Window.DataContext as YourViewModel).Control = (UserControl)ControlView;
if (ControlViewModel != null)
(Window.DataContext as YourViewModel).Control.DataContext = ControlViewModel;
if (ShowAsDialog) //with interface use cast to call the show...
Window.ShowDialog();
else
Window.Show();
}
}
What you can do is, place the Emergency usercontrol inside MainGrid and control its visibility through Model property.
<Canvas Background="DarkGray">
<!--Main Grid-->
<Grid Width="1080" Height="1920" Background="White" Name="MainGrid"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<StackPanel Background="Black">
<Grid Background="#253341">
<Grid.RowDefinitions>
<RowDefinition Height="5"/>
<RowDefinition Height="*"/>
<RowDefinition Height="5"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="5"/>
<ColumnDefinition Width="264"/>
</Grid.ColumnDefinitions>
<Grid Grid.Row="1" Grid.Column="1">
<Button Tag="{StaticResource EmergencyImg}" Name="EmergencyButton"
Command="{Binding ShowEmergencyPanel}">
<Image Source="{StaticResource EmergencyImg}" />
</Button>
</Grid>
</Grid>
</StackPanel>
<Grid Name="EmergencyPanel">
<view1:EmergencyInfo x:Name="EmergencyInfoPanel" Visibility={Binding IsEmergencyPanelVisible,Converter={StaticResource BoolToVisibilityConverter}} DataContext={Binding} />
</Grid>
</Grid>
<!--Main Grid-->
</Canvas>
By setting proper background to Grid that holding usercontrol, you can achieve modal window like effect.
And you need to have IsEmergencyPanelVisible(default value is false) is your Model, and this property should be changed to true on your button click command.
Note : I guess you are familiar with converters, so i included Converters in my solution.
I have a listbox in which I render data using multiple datatemplates, and I use a datatemplate selector to route data to the appropriate template.
Each template has it's own layout using a grid. The first column of every grid inside the template is a textblock and I want them aligned to left. The next item is another textblock which should be aligned towards the maximum width of the first textblock (something similar to a data entry form). I'm using Grid.IsSharedSizeScope for this, but not able to achieve this. Below is my code:
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Page.Resources>
<DataTemplate x:Key="DefaultTemplate">
<Border BorderThickness="1" CornerRadius="3" BorderBrush="LightGray">
<TextBlock Text="{Binding Name}"></TextBlock>
</Border>
</DataTemplate>
<DataTemplate x:Key="ShortFieldTemplate">
<Grid Grid.IsSharedSizeScope="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="A"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Text="Age:" Grid.Column="0"></TextBlock>
<TextBlock Text="{Binding Age}" Grid.Column="1"></TextBlock>
</Grid>
</DataTemplate>
<DataTemplate x:Key="LongFieldTemplate">
<Grid Grid.IsSharedSizeScope="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="A"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Text="This should be the name:" Grid.Column="0"></TextBlock>
<TextBlock Text="{Binding Name}" Grid.Column="1"></TextBlock>
</Grid>
</DataTemplate>
<GridSplitterTextTrim:MyFirstTemplateSelector x:Key="MyFirstTemplateSelector"
DefaultDataTemplate="{StaticResource DefaultTemplate}"
ShortFieldDataTemplate="{StaticResource ShortFieldTemplate}"
LongFieldDataTemplate="{StaticResource LongFieldTemplate}"
/>
</Page.Resources>
<Grid x:Name="LayoutRoot" Grid.IsSharedSizeScope="True">
<Grid Grid.Column="2" Background="Green" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<ListBox ItemsSource="{Binding Items}" Grid.Row="0" Grid.Column="0" x:Name="listbox" ItemTemplateSelector="{StaticResource MyFirstTemplateSelector}">
</ListBox>
</Grid>
</Grid>
</Page>
..and my object model
public class ShortField : INotifyPropertyChanged
{
private string _name;
public string Age
{
get { return _name; }
set
{
_name = value;
InvokePropertyChanged(new PropertyChangedEventArgs("Age"));
}
}
private string _currentValue;
public string CurrentValue
{
get { return _currentValue; }
set
{
_currentValue = value;
InvokePropertyChanged(new PropertyChangedEventArgs("CurrentValue"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void InvokePropertyChanged(PropertyChangedEventArgs e)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, e);
}
public static List<ShortField> GetShortFields()
{
return new List<ShortField>()
{
new ShortField() {Age = "10"},
new ShortField() {Age = "21"},
};
}
}
How do I get this alignment right? I thought Grid.IsSharedScope should do the trick. Is that correct or is there any other way?
Thanks in advance,
-Mike
try to set
<ListBox Grid.IsSharedSizeScope="True"
So I'm having what should be a simple XAML Data Binding error. I've got the below XAML with two classes (so far) that they used for DataBinding, a Row, which contains an ObservableCollection rows. The nodes have a bunch of extra associated information and I'm trying to show these nodes in a grid-like fashion (it's going to be used for a path-finding algorithm.)
The problem is that the "Here" TextBlock doesn't show up. But I know that the Nodes are getting bound properly because their values show up in the Debugging StackPanel.
<Window.Resources>
<local:Row x:Key="TestRow">
<local:Node x="0" y="0" Cost="20" />
<local:Node x="0" y="1" Cost="20" />
<local:Node x="0" y="2" Cost="20" />
</local:Row>
</Window.Resources>
<Grid Name="MainGrid" Margin="10,10,10,10" >
<Grid.RowDefinitions>
<RowDefinition Height="150" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<StackPanel Margin="0,25,0,0"
DataContext="{Binding ElementName=AStarListView,
Path=SelectedItem}"
x:Name="Debugging" Orientation="Vertical" >
<TextBlock Text="{Binding x}" />
<TextBlock Text="{Binding y}" />
<TextBlock Text="{Binding Cost}" />
</StackPanel>
<ListView Grid.RowSpan="2" Margin="2,2,2,2" Grid.Column="1"
x:Name="AStarListView"
ItemsSource="{StaticResource TestRow}" >
<ListView.ItemTemplate>
<DataTemplate DataType="local:Row">
<ListView ItemsSource="{Binding nodes}" Width="48" Height="48" >
<ListView.ItemTemplate>
<DataTemplate DataType="local:Node">
<TextBlock Text="Here" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Here's the (stripped) Node class.
public class Node : INotifyPropertyChanged
{
public Tuple<int, int> coordinates { get; set; }
public int x
{
get
{
if (this.coordinates == null)
return -1;
return this.coordinates.Item1;
}
set
{
if (this.coordinates != null)
this.coordinates = new Tuple<int, int>(value, y);
else
this.coordinates = new Tuple<int, int>(value, -1);
OnPropertyChanged("x");
}
}
public int y
{
get
{
if (this.coordinates == null)
return -1;
return this.coordinates.Item2;
}
set
{
if (this.coordinates != null)
this.coordinates = new Tuple<int, int>(x, value);
else
this.coordinates = new Tuple<int, int>(-1, value);
OnPropertyChanged("y");
}
}
private Node _parent;
private int _Cost;
public int Cost
{
get
{
return _Cost;
}
set
{
_Cost = value;
OnPropertyChanged("Cost");
}
}
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChangedEventArgs args =
new PropertyChangedEventArgs(propertyName);
this.PropertyChanged(this, args);
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
Here's the Row class:
public class Row : ObservableCollection<Node>
{
public ObservableCollection<Node> nodes { get; set; }
public Row()
{
this.nodes = new ObservableCollection<Node>();
}
}
Here's the corrected XAML, the class definitions are correct in the question, need to replace "AStarListView" with this XAML.
<ListView Grid.RowSpan="2" Margin="2,2,2,2" Grid.Column="1" x:Name="AStarListView"
ItemsSource="{StaticResource TestRow}" >
<ListView.ItemTemplate>
<DataTemplate DataType="local:Node">
<Grid Background="#dddddd" >
<TextBlock Grid.Row="0" Grid.Column="0" Text="{Binding x}"/>
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding y}"/>
<TextBlock Grid.Row="1" Grid.Column="0" Text="{Binding Cost}"/>
<Grid.RowDefinitions>
<RowDefinition Height="24"/>
<RowDefinition Height="24"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="24"/>
<ColumnDefinition Width="24"/>
</Grid.ColumnDefinitions>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
My problem was that I had the ListViews too deeply nested. The inner ListView was binding to the "nodes" property of a Node, which didn't exist.