Reputation: 805
I've been playing around with this for a while and whilst I can get my design time data to bind properly, I can't seem to get the runtime data to bind. I've spent a fair while looking at lots of examples on the net for all manner of ways to data bind, but haven't come across any that are doing it quite like this.
My thinking is that the Window is bound to itself using RelativeSource, which should then allow me to bind the grid to a property of the Window, i.e. MyWidget. I think I've replicated this with my design time data, so I'm not really sure why the designer works, but when I run it I just get a blank window...
Here's my XAML window:
<Window x:Class="XamlPrototype.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:XamlPrototype"
mc:Ignorable="d"
Title="MainWindow" Height="646" Width="344"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
d:DataContext="{Binding Source={d:DesignInstance Type=local:DesignTimeWidgetData, IsDesignTimeCreatable=True}}">
<Grid x:Name="MyGrid"
DataContext="{Binding MyWidget}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="20" />
<RowDefinition Height="10" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock FontSize="20" VerticalAlignment="Center" FontWeight="Medium" Grid.Row="0" Margin="20,20,0,0" Text="{Binding Title}" />
<TextBlock FontSize="12" VerticalAlignment="Top" Grid.Row="1" Margin="20,0,0,0" Text="{Binding Subtitle}" />
<StackPanel Grid.Row="3">
<ItemsControl
x:Name="ItemsControl"
ItemsSource="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="20,20,0,0">
<Rectangle Stroke="Red" StrokeThickness="5" Width="25" Height="25" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="0,0,0,0"></Rectangle>
<TextBlock FontSize="20" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="10,0,0,5" Text="{Binding}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Grid>
..its code behind:
public partial class MainWindow : Window
{
public Widget MyWidget { get; set; }
public MainWindow()
{
InitializeComponent();
InitializeWidgetData();
}
private void InitializeWidgetData()
{
MyWidget = new Widget
{
Title = "This is the Title",
Subtitle = "This is the subtitle",
Items = new List<string>
{
"Item Short Name 1",
"Item Short Name 2",
"Item Short Name 3",
"Item Short Name 4",
"Item Short Name 5",
"Item Short Name 6",
"Item Short Name 7",
"Item Short Name 8"
}
};
}
}
The data context:
public class Widget
{
public string Title { get; set; }
public string Subtitle { get; set; }
public List<string> Items { get; set; }
}
and my design time data:
public class DesignTimeWidgetData
{
public Widget MyWidget { get; set; }
public DesignTimeWidgetData()
{
MyWidget = new Widget
{
Title = "Design Time Title",
Subtitle = "Design Time Subtitle",
Items = new List<string>
{
"Design Time Short Name 1",
"Design Time Short Name 2",
"Design Time Short Name 3",
"Design Time Short Name 4",
"Design Time Short Name 5",
"Design Time Short Name 6",
"Design Time Short Name 7",
"Design Time Short Name 8"
}
};
}
}
UPDATE:
Following HighCore's suggestion and example, and I bit of extra Googling, I have amended the project as follows:
The App XAML:
<Application x:Class="XamlPrototype.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:XamlPrototype"
StartupUri="MainWindow.xaml">
<Application.Resources>
<local:WindowViewModel x:Key="WindowViewModel" />
</Application.Resources>
The Window XAML:
<Window x:Class="XamlPrototype.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:XamlPrototype"
mc:Ignorable="d"
Title="MainWindow" Height="646" Width="344"
DataContext="{StaticResource WindowViewModel}"
d:DataContext="{Binding Source={d:DesignInstance Type=local:DesignTimeViewModel, IsDesignTimeCreatable=True}}">
<Grid x:Name="MyGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="20" />
<RowDefinition Height="10" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock FontSize="20" VerticalAlignment="Center" FontWeight="Medium" Grid.Row="0" Margin="20,20,0,0" Text="{Binding WindowTitle}" />
<TextBlock FontSize="12" VerticalAlignment="Top" Grid.Row="1" Margin="20,0,0,0" Text="{Binding WindowSubtitle}" />
<StackPanel Grid.Row="3">
<ItemsControl
x:Name="ItemsControl"
ItemsSource="{Binding WindowItems}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="20,20,0,0">
<Rectangle Stroke="Red" StrokeThickness="5" Width="25" Height="25" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="0,0,0,0"></Rectangle>
<TextBlock FontSize="20" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="10,0,0,5" Text="{Binding}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Grid>
..and its code behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
The ViewModel:
public class WindowViewModel : INotifyPropertyChanged
{
private Widget _widget;
public string WindowTitle
{
get { return _widget.Title ?? "Error!"; }
}
public string WindowSubtitle
{
get { return _widget.Subtitle ?? "Error!"; }
}
public List<string> WindowItems
{
get { return _widget.Items ?? new List<string>(); }
}
public WindowViewModel()
{
//in reality this would come from the Db, or a service perhaps..
_widget = new Widget
{
Title = "This is the Title",
Subtitle = "This is the subtitle",
Items = new List<string>
{
"Item Short Name 1",
"Item Short Name 2",
"Item Short Name 3",
"Item Short Name 4",
"Item Short Name 5",
"Item Short Name 6",
"Item Short Name 7",
"Item Short Name 8"
}
};
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
// take a copy to prevent thread issues
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
The design time (viewmodel) data:
public class DesignTimeViewModel
{
public string WindowTitle { get; set; }
public string WindowSubtitle { get; set; }
public List<string> WindowItems { get; set; }
public DesignTimeViewModel()
{
var widget = new Widget
{
Title = "Design Time Title",
Subtitle = "Design Time Subtitle",
Items = new List<string>
{
"Design Time Short Name 1",
"Design Time Short Name 2",
"Design Time Short Name 3",
"Design Time Short Name 4",
"Design Time Short Name 5",
"Design Time Short Name 6",
"Design Time Short Name 7",
"Design Time Short Name 8"
}
};
WindowTitle = widget.Title;
WindowSubtitle = widget.Subtitle;
WindowItems = widget.Items;
}
}
The widget data remains the same.
Upvotes: 0
Views: 443
Reputation: 44028
Quick Solution:
Swap the order of these lines in your constructor:
public MainWindow()
{
InitializeWidgetData(); // This first
InitializeComponent(); // This second
}
Real Solution:
Create a proper ViewModel that implements INotifyPropertyChanged
to bind to, as opposed to binding the window to itself.
Upvotes: 2