I have 2 user controls one named Filters and one named FilterItem
Filter looks like this:
<UserControl xmlns:my="clr-namespace:AttorneyDashboard.Views.UserControls" x:Class="AttorneyDashboard.Views.UserControls.Filters"
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:helpers="clr-namespace:AttorneyDashboard.Helpers"
mc:Ignorable="d"
d:DesignHeight="150" d:DesignWidth="590" x:Name="FiltersRoot">
<Grid>
<ListBox x:Name="myListBox" ItemsSource="{Binding Path=FilterItems, ElementName=FiltersRoot}" >
<ListBox.ItemTemplate>
<DataTemplate>
<my:FilterItem ColumnsList="{Binding Path=Columns_, ElementName=FiltersRoot}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</UserControl>
Code Behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Data;
using System.Collections.ObjectModel;
using System.Diagnostics;
using AttorneyDashboard.Helpers;
namespace AttorneyDashboard.Views.UserControls
{
public class MyItems
{
public string Header { get; set; }
}
public partial class Filters : UserControl
{
public Filters()
{
InitializeComponent();
}
private DependencyProperty FilterItemsProperty = DependencyProperty.Register("FilterItems", typeof(ObservableCollection<FilterDescriptor>), typeof(Filters), new PropertyMetadata(null, new PropertyChangedCallback(OnChangeFilterItems)));
public ObservableCollection<FilterDescriptor> FilterItems
{
get
{
return (ObservableCollection<FilterDescriptor>)GetValue(FilterItemsProperty);
}
set
{
SetValue(FilterItemsProperty, value);
}
}
public static void OnChangeFilterItems(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
public List<MyItems> Columns_
{
get
{
List<MyItems> list = new List<MyItems>();
list.Add(new MyItems() { Header = "test1" });
list.Add(new MyItems() { Header = "test2" });
list.Add(new MyItems() { Header = "test3" });
return list;
}
}
}
}
FilterItems looks like this
<UserControl x:Class="AttorneyDashboard.Views.UserControls.FilterItem"
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"
d:DesignHeight="23" d:DesignWidth="590" xmlns:my="clr-namespace:AttorneyDashboard.Helpers" x:Name="FilterItemRoot">
<StackPanel Orientation="Horizontal">
<ComboBox Height="23" HorizontalAlignment="Left" Name="FieldName" VerticalAlignment="Top" Width="120" Margin="5,0,0,0" ItemsSource="{Binding Path=ColumnsList, ElementName=FilterItemRoot}" SelectedItem="{Binding PropertyPath, Mode=TwoWay}" DisplayMemberPath="Header"/>
</StackPanel>
</UserControl>
Code behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.ObjectModel;
using AttorneyDashboard.Helpers;
using System.Windows.Data;
namespace AttorneyDashboard.Views.UserControls
{
public partial class FilterItem : UserControl
{
public FilterItem()
{
InitializeComponent();
}
private DependencyProperty ColumnsListProperty = DependencyProperty.Register("ColumnsList", typeof(List<MyItems>), typeof(FilterItem), new PropertyMetadata(null, new PropertyChangedCallback(OnChangeColumns)));
public List<MyItems> ColumnsList
{
get
{
return (List<MyItems>)GetValue(ColumnsListProperty);
}
set
{
SetValue(ColumnsListProperty, value);
}
}
public static void OnChangeColumns(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
}
}
The number of FilterItems is ok (this means that FilterItems binding works ok), but only the Combobox of the last FilterItem is populated...
And I do not know what exactly is wrong...
Update:
I found why but I stll do not know a solution...
It seams that the content of FilterItem is binded before his properties are..
So the combobox in FilterItem is binded before the Columns property is binded...
Your code in FilterItem
private DependencyProperty ColumnsListProperty = DependencyProperty
.Register("ColumnsList", typeof(List<MyItems>), typeof(FilterItem),
new PropertyMetadata(null, new PropertyChangedCallback(OnChangeColumns)));
Please, make it static:
private **static** DependencyProperty ColumnsListProperty = DependencyProperty
.Register("ColumnsList", typeof(List<MyItems>), typeof(FilterItem),
new PropertyMetadata(null, new PropertyChangedCallback(OnChangeColumns)));
Thats it.
P.S. : in Filters make dependency property static too, generally do it anywhere :)
You have placed a x:Name attribute directly on your UserControl elements. Don't do that. Use this pattern instead:-
<UserControl x:Class="AttorneyDashboard.Views.UserControls.FilterItem"
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"
d:DesignHeight="23" d:DesignWidth="590" xmlns:my="clr-namespace:AttorneyDashboard.Helpers" >
<StackPanel Orientation="Horizontal" x:Name="LayoutRoot">
<ComboBox Height="23" HorizontalAlignment="Left" Name="FieldName" VerticalAlignment="Top" Width="120" Margin="5,0,0,0" ItemsSource="{Binding Path=Parent.ColumnsList, ElementName=LayoutRoot}" SelectedItem="{Binding PropertyPath, Mode=TwoWay}" DisplayMemberPath="Header"/>
</StackPanel>
</UserControl>
You are not in control of name assigned to the user control, that belongs in the scope of the Xaml the uses your UserControl. If your code internally requires that the containing UserControl have a specific name then things are likely to break.
Related
I am a newbie to WPF and while I have read lots of theory and articles, I am unable to put it all together in a working solution.
Presently, I wish to implement dynamic multiple views in a window which could be selected by the user using buttons. The target is very much like one given in the question,
WPF : dynamic view/content
Can somebody please share with me a working code, of simplest implementation of the above. Just a window which contains two grids - one grid has two buttons - second grid changes background color depending on which button is clicked. From there on , I can take things further.
Thank you very much.
Use MVVM
It's a design approach. Basically you treat your Window as shell, and it's responsible for swapping views.
To simplify this snippet, I've referenced MvvmLight .
The Window contains ContentControl which dynamically displays the relevant view
Each dynamic view can communicate with the shell Window (using MvvmLight's Messenger) and tell it to change the view to something else.
MainWindow.xaml
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:MainWindowViewModel></local:MainWindowViewModel>
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="20"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Button Grid.Row="0" Grid.Column="0" Command="{Binding ChangeFirstViewCommand}">Change View #1</Button>
<Button Grid.Row="0" Grid.Column="1" Command="{Binding ChangeSecondViewCommand}">Change View #2</Button>
<ContentControl Grid.Row="1" Grid.ColumnSpan="2" Content="{Binding ContentControlView}"></ContentControl>
</Grid>
</Window>
MainWindowViewModel.cs
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
namespace WpfApplication1
{
public class MainWindowViewModel : ViewModelBase
{
private FrameworkElement _contentControlView;
public FrameworkElement ContentControlView
{
get { return _contentControlView; }
set
{
_contentControlView = value;
RaisePropertyChanged("ContentControlView");
}
}
public MainWindowViewModel()
{
Messenger.Default.Register<SwitchViewMessage>(this, (switchViewMessage) =>
{
SwitchView(switchViewMessage.ViewName);
});
}
public ICommand ChangeFirstViewCommand
{
get
{
return new RelayCommand(() =>
{
SwitchView("FirstView");
});
}
}
public ICommand ChangeSecondViewCommand
{
get
{
return new RelayCommand(() =>
{
SwitchView("SecondView");
});
}
}
public void SwitchView(string viewName)
{
switch (viewName)
{
case "FirstView":
ContentControlView = new FirstView();
ContentControlView.DataContext = new FirstViewModel() { Text = "This is the first View" };
break;
default:
ContentControlView = new SecondView();
ContentControlView.DataContext = new SecondViewModel() { Text = "This is the second View" };
break;
}
}
}
}
FirstView.xaml
<UserControl x:Class="WpfApplication1.FirstView"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<StackPanel>
<Label>This is the second view</Label>
<Label Content="{Binding Text}" />
<Button Command="{Binding ChangeToSecondViewCommand}">Change to Second View</Button>
</StackPanel>
</UserControl>
FirstViewModel.cs
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace WpfApplication1
{
public class FirstViewModel : ViewModelBase
{
private string _text;
public string Text
{
get { return _text; }
set
{
_text = value;
RaisePropertyChanged("Text");
}
}
public ICommand ChangeToSecondViewCommand
{
get
{
return new RelayCommand(() =>
{
Messenger.Default.Send<SwitchViewMessage>(new SwitchViewMessage { ViewName = "SecondView" });
});
}
}
}
}
SecondView.xaml
<UserControl x:Class="WpfApplication1.SecondView"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<StackPanel>
<Label>This is the second view</Label>
<Label Content="{Binding Text}" />
<Button Command="{Binding ChangeToFirstViewCommand}">Change to First View</Button>
</StackPanel>
</UserControl>
SecondViewModel.cs
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace WpfApplication1
{
public class SecondViewModel : ViewModelBase
{
private string _text;
public string Text
{
get { return _text; }
set
{
_text = value;
RaisePropertyChanged("Text");
}
}
public ICommand ChangeToFirstViewCommand
{
get
{
return new RelayCommand(() =>
{
Messenger.Default.Send<SwitchViewMessage>(new SwitchViewMessage { ViewName = "FirstView" });
});
}
}
}
}
SwitchViewMessage.cs
namespace WpfApplication1
{
public class SwitchViewMessage
{
public string ViewName { get; set; }
}
}
Trying to bind a listbox to an object. Code runs without errors but for some reason sample data doesn't appear in listbox
XAML: ucDataBindingObject.xaml
<UserControl x:Class="TheProject.UserControls.ucDataBindingObject"
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"
Name="DataBindingObject"
Width="Auto"
Height="Auto"
mc:Ignorable="d">
<Grid Width="130"
Height="240"
Margin="0">
<ListBox Name="lbObject"
Width="110"
Height="80"
Margin="10,7,-9.6,0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
DisplayMemberPath="Name"
ItemsSource="{Binding ElementName=ucDataBindingObject,
Path=Clients}" />
</Grid>
</UserControl>
C#: ucDataBindingObject.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Controls;
namespace TheProject.UserControls
{
public partial class ucDataBindingObject : UserControl
{
public List<Client> Clients { get; set; }
public ucDataBindingObject()
{
Clients = new List<Client>();
Clients.Add(new Client(1, "David")); // sample data
Clients.Add(new Client(2, "Helen"));
Clients.Add(new Client(3, "Joe"));
InitializeComponent();
}
}
C# Client.cs
using System;
using System.Linq;
namespace TheProject.UserControls
{
public class Client
{
public int ID { get; set; }
public string Name { get; set; }
public Client(int id, string name)
{
this.ID = id;
this.Name = name;
}
}
}
Update your ItemsSource Binding as
ItemsSource="{Binding Path=Clients}"
and in the constructor of your view, set its DataContext after InitializeComponents as
this.DataContext = this;
there is no element named ucDataBindingObject, its the class name of your usercontrol
OR change the elementname in binding to DataBindingObject, which you named your usercontrol
In the following XAML UserControl I am binding a few items to properties in the UserControl's linked class.
<UserControl x:Class="Kiosk.EventSelectButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Kiosk"
Height="130" Width="130">
<Grid>
<Button
Style="{DynamicResource DarkButton130x130}"
HorizontalAlignment="Left"
VerticalAlignment="Center">
<Grid Margin="0,0,0,0" Height="118" Width="118">
<Image VerticalAlignment="Top" HorizontalAlignment="Center" Source="image/select_button_arrows.png" />
<Image x:Name="EventImageComponent" VerticalAlignment="Center" HorizontalAlignment="Center" Effect="{DynamicResource KioskStandardDropShadow}" Source="{Binding Path=EventImage}" />
<TextBlock x:Name="SelectTextBlock" Text="{Binding Path=SelectText}" VerticalAlignment="Top" HorizontalAlignment="Center" Margin="0,-2,0,0" FontSize="10pt" Foreground="#5aaff5" />
<TextBlock x:Name="LabelTextBlock" VerticalAlignment="Bottom" HorizontalAlignment="Right" Margin="0,0,0,0" FontSize="14pt" FontWeight="Bold" Text="{Binding Path=Label}"/>
</Grid>
</Button>
</Grid>
</UserControl>
In the linked class' contstructor I'm applying the DataContext of the items to this, as you can see below.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Kiosk
{
/// <summary>
/// Interaction logic for EventSelectButton.xaml
/// </summary>
public partial class EventSelectButton : UserControl
{
public String ValueContainer;
private String _EventImage;
public String EventImage
{
get
{
return _EventImage;
}
set
{
_EventImage = value;
}
}
private String _Label;
public String Label
{
get
{
return _Label;
}
set
{
_Label = value;
}
}
private String _SelectText;
public String SelectText
{
get
{
return _SelectText;
}
set
{
_SelectText = value;
}
}
public EventSelectButton()
{
InitializeComponent();
LabelTextBlock.DataContext = this;
SelectTextBlock.DataContext = this;
EventImageComponent.DataContext = this;
}
}
}
Edit
Although this works as intended, I'm interested to know if there is a simpler way of doing this. (edit, lessons learned.) This won't actually work beyond the initialisation, the public properties will be set, however because the class doesn't use DependentProperties or alternatively, implement INotifyPropertyChanged, binding will not work as expected. (end edit)
For example,
Can I set the DataContext of these items in the XAML to this (as the EventSelectButton instance), and if so, how?
Alternatively, is it possible to inherit the DataContext from the UserControl parent, thus making the Binding Paths simpler.
The only alternatives I've found so far are more verbose, e.g. using the RelativeSource binding method to locate the EventSelectButton Ancestor.
So please, let me know any ways I can improve this binding expression, and any comments on best practices for binding within a UserComponent are much appreciated.
One way is to do the following:
Name your UserControl in your XAML.
Bind the DataContext of the root element (i.e. Grid) to the UserControl.
Like this:
<UserControl x:Name="uc">
<Grid DataContext="{Binding ElementName=uc}">
.
.
.
</Grid>
</UserControl>
Now, you'll ask, why not just set the DataContext of the UserControl itself? Well, this just ensures that setting the DataContext of an instance of the UserControl will still work without affecting the bindings in the UserControl's visual tree. So something like the one below will still work fine.
<Window>
<uc:EventSelectButton DataContext="{Binding SomeDataContext}" Width="{Binding SomeDataContextWidth}"/>
</Window>
EDIT
To make the solution complete requires the properties in the UserControl to be changed to use DependencyProperty objects instead. Below are the updates to the codes:
XAML:
<UserControl x:Class="Kiosk.EventSelectButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Kiosk"
x:Name="root"
Height="130" Width="130">
<Grid x:Name="LayoutRoot" DataContext="{Binding ElementName=root}">
<Button
Style="{DynamicResource DarkButton130x130}"
HorizontalAlignment="Left"
VerticalAlignment="Center">
<Grid Margin="0,0,0,0" Height="118" Width="118" >
<Image VerticalAlignment="Top" HorizontalAlignment="Center" Source="image/select_button_arrows.png" />
<Image Source="{Binding EventImage}" VerticalAlignment="Center" HorizontalAlignment="Center" Effect="{DynamicResource KioskStandardDropShadow}" />
<TextBlock Text="{Binding SelectText}" VerticalAlignment="Top" HorizontalAlignment="Center" Margin="0,-2,0,0" FontSize="10pt" Foreground="#5aaff5" />
<TextBlock Text="{Binding Label}" VerticalAlignment="Bottom" HorizontalAlignment="Right" Margin="0,0,0,0" FontSize="14pt" FontWeight="Bold" />
</Grid>
</Button>
</Grid>
</UserControl>
Code-Behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Kiosk
{
public partial class EventSelectButton : UserControl
{
public static readonly DependencyProperty EventImageProperty =
DependencyProperty.Register(
"EventImage",
typeof(string),
typeof(EventSelectButton));
public String EventImage
{
get { return (string)GetValue(EventImageProperty); }
set { SetValue(EventImageProperty, value); }
}
public static readonly DependencyProperty SelectTextProperty =
DependencyProperty.Register(
"SelectText",
typeof(string),
typeof(EventSelectButton));
public String SelectText
{
get { return (string)GetValue(SelectTextProperty); }
set { SetValue(SelectTextProperty, value); }
}
public static readonly DependencyProperty LabelProperty =
DependencyProperty.Register(
"Label",
typeof(string),
typeof(EventSelectButton));
public String Label
{
get { return (string)GetValue(LabelProperty); }
set { SetValue(LabelProperty, value); }
}
public EventSelectButton()
{
InitializeComponent();
}
}
}
The following example successfully binds objects with a ListBox to display them.
However, I would like to create all the objects in one class and then from another class query them with LINQ to fill my XAML ListBox, what would I need to add this example:
XAML:
<Window x:Class="WpfApplication15.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300"
xmlns:local="clr-namespace:WpfApplication15">
<Window.Resources>
<ObjectDataProvider x:Key="customers" ObjectType="{x:Type local:Customers}"/>
<DataTemplate x:Key="LastNameFirst" DataType="WpfApplication15.Customer">
<StackPanel Margin="10 10 10 0" Orientation="Horizontal">
<TextBlock Text="{Binding Path=LastName}" FontWeight="bold"/>
<TextBlock Text=", "/>
<TextBlock Text="{Binding Path=FirstName}"/>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<ListBox ItemsSource="{Binding Source={StaticResource customers}}"
ItemTemplate="{StaticResource LastNameFirst}"/>
</Grid>
</Window>
Code behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApplication15
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
}
public class Customer
{
public string FirstName { get; set; }
public string LastName { get; set; }
public Customer(string firstName, string lastName)
{
this.FirstName = firstName;
this.LastName = lastName;
}
}
public class Customers : List<Customer>
{
public Customers()
{
this.Add(new Customer("Jim", "Thompson"));
this.Add(new Customer("Julie", "Watson"));
this.Add(new Customer("John", "Walton"));
}
}
}
edit: added ToList call to the LINQ query
You can just assign the ItemsSource of the ListBox using LINQ in code-behind for this. Assuming you give your ListBox a name:
<Window x:Class="WpfApplication15.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:local="clr-namespace:WpfApplication15"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="300" Height="300" Title="Window1">
<Window.Resources>
<DataTemplate x:Key="LastNameFirst" DataType="WpfApplication15.Customer">
<StackPanel Margin="10 10 10 0" Orientation="Horizontal">
<TextBlock FontWeight="bold" Text="{Binding Path=LastName}"/>
<TextBlock Text=", "/>
<TextBlock Text="{Binding Path=FirstName}"/>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<ListBox x:Name="lstCustomers"
ItemTemplate="{StaticResource LastNameFirst}"/>
</Grid>
</Window>
You can assign to ItemsSource in the Loaded event:
public partial class Window1 : Window
{
public Window1()
{
this.Loaded += new RoutedEventHandler(Window1_Loaded);
InitializeComponent();
}
void Window1_Loaded(object sender, RoutedEventArgs e)
{
Customers customers = new Customers();
lstCustomers.ItemsSource = customers.Where(customer => customer.LastName.StartsWith("W")).ToList();
}
}
Assuming your LINQ query will change depending on some logic, you can re-assign ItemsSource at the appropriate points.
If you want to bind without dipping into code-behind whenever your query logic changes, you're probably better off using a CollectionViewSource, since it has sorting and filtering capabilities (assuming that's what you're after from using LINQ).
Take a look # http://www.codeplex.com/bindablelinq and you should find a bunch of good examples for this.
I have UserControl which contains a TextBox and a Button control. The Button opens a FileDialog and the user selects the file. The selected file is transferred into the FileName property which is a dependency property. For some reason the TextBox is not binding to this property. Here is the code:
<UserControl x:Class="WPF3D.FileInputBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="300" Width="300" x:Name="root">
<Grid>
<StackPanel>
<TextBox Name="txtFile" Text="{Binding FileName, ElementName=root}" Width="300" Height="20" />
<Button Content="Select File" Width="100" Height="20" Click="SelectFile" />
</StackPanel>
</Grid>
</UserControl>
And here is the code for the UserControl.cs file:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Microsoft.Win32;
using System.Windows.Markup;
namespace WPF3D
{
[ContentProperty("FileName")]
public partial class FileInputBox : UserControl
{
public static readonly DependencyProperty FileNameProperty = DependencyProperty.Register("FileName",
typeof (String),
typeof (FileInputBox));
public event EventHandler<EventArgs> FileNameChanged;
public FileInputBox()
{
InitializeComponent();
txtFile.TextChanged += new TextChangedEventHandler(txtFile_TextChanged);
}
void txtFile_TextChanged(object sender, TextChangedEventArgs e)
{
e.Handled = true;
if(FileNameChanged != null)
{
FileNameChanged(this, EventArgs.Empty);
}
}
public string FileName
{
get { return (string) GetValue(FileNameProperty); }
set { SetValue(FileNameProperty,value);}
}
private void SelectFile(object sender, RoutedEventArgs e)
{
// select the file
var fileDialog = new OpenFileDialog();
fileDialog.ShowDialog();
this.FileName = fileDialog.FileName;
}
protected override void OnContentChanged(object oldContent, object newContent)
{
if(oldContent != null)
throw new InvalidOperationException("You can't change the content");
}
}
}
I think this is just a scoping issue as evident by this output in the debug window:
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'ElementName=root'. BindingExpression:Path=FileName; DataItem=null; target element is 'TextBox' (Name='txtFile'); target property is 'Text' (type 'String')
If you just change it to this, it works fine:
<UserControl x:Class="TestApp.FileInputBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="300" Width="300">
<Grid x:Name="_grid">
<StackPanel>
<TextBox Name="txtFile" Text="{Binding FileName}" Width="300" Height="20" />
<Button Content="Select File" Width="100" Height="20" Click="SelectFile" />
</StackPanel>
</Grid>
</UserControl>
And the important part of the code-behind:
public FileInputBox()
{
InitializeComponent();
txtFile.TextChanged += new TextChangedEventHandler(txtFile_TextChanged);
_grid.DataContext = this;
}
Note that setting the DataContext on the Grid rather than the UserControl is intentional. If you do it at the UserControl level it becomes possible for consumers of your control to break your bindings simply by changing the DataContext of your UserControl.