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();
}
}
}
Related
This question is related to removing an attribute in xaml.
In the below code, I have a Span. During an event, I add a back ground to this Span. During another event, I need to remove it. Please let me know, if there is a way to remove the background attribute I set to Span.
My Xaml looks like this.
<Window x:Class="WpfApplication4.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:WpfApplication4"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition Height="40"></RowDefinition>
</Grid.RowDefinitions>
<RichTextBox x:Name="rtb">
<FlowDocument>
<Paragraph>
<Span x:Name="def" Tag="default">
<Run x:Name="deg">Some Text</Run>
</Span>
</Paragraph>
</FlowDocument>
</RichTextBox>
<TextBox x:Name="tx" Grid.Row="1" TextWrapping="Wrap"/>
<Grid Grid.Row="2">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Button Content="s" Click="Button_Click"/>
<Button Content="bg-Add" Grid.Column="1" Click="Button_Click"/>
<Button Content="bg-Remove" Grid.Column="2" Click="Button_Click"/>
</Grid>
</Grid>
</Window>
My code looks like this.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows. Shapes;
namespace WpfApplication4
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
if ((sender as Button).Content.ToString() == "s")
{
tx.Text = XamlWriter.Save(def);
}
else if ((sender as Button).Content.ToString() == "bg-Add")
{
def.Background = new SolidColorBrush(Colors.Blue);
}
else if ((sender as Button).Content.ToString() == "bg-Remove")
{
//Need to remove the set back color so that I get the default back
}
}
}
}
I am able to find an answer myself.
def.ClearValue(Span.BackgroundProperty);
is going to remove that attribute for me.
You can override ShouldSerializeProperty
public class CustomizedSpan : Span
{
public bool IsRemoveBackGround;
protected override bool ShouldSerializeProperty(DependencyProperty dp)
{
if (dp == Span.BackgroundProperty && IsRemoveBackGround)
{
return false;
}
else
{
return base.ShouldSerializeProperty(dp);
}
}
}
My grid displays a button in a column. By default, Silverlight disables Tooltip display for disabled button. I've tried to work around this by placing button and tooltip object inside of border control. I want to show tooltip when button disabled and not show when enabled. I tried to bind tooltip to button IsEnabled property but doesn't work. Here is code:
<Border HorizontalAlignment="Center" VerticalAlignment="Center" Background="Transparent" >
<ToolTipService.ToolTip >
<ToolTip Content="Ticket Required" Visibility="{Binding IsEnabled, ElementName=btnEdit, Converter={StaticResource BooleanToInvisibilityConverter}}" />
</ToolTipService.ToolTip>
<Button x:Name="btnEdit" Content="Add" Margin="0, 0, 7, 0" Width="35" HorizontalAlignment="Right" Command="{Binding EditCommand}" CommandParameter="{Binding}" Style="{StaticResource SquareButtonStyle}" Click="EditButtonClick" IsTabStop="True"/>
</Border>
The tooltip displays regardless whether the button is enabled/disabled. What am I doing wrong. I sense something wrong with my binding or is this not the way to do it. Much thanks.
It seems that element binding does not work here for some reason. The binding mechanism cannot find the button you are trying to bind to. It works if you bind to a property on, say, a viewmodel instead. Here is a working example:
<UserControl x:Class="SilverlightApplication14.MainPage"
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:SilverlightApplication14"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<Grid x:Name="LayoutRoot"
Background="White">
<Grid.Resources>
<local:ReverseBoolToVisibilityConverter x:Key="ReverseBoolToVisibilityConverter"></local:ReverseBoolToVisibilityConverter>
</Grid.Resources>
<Border HorizontalAlignment="Center"
VerticalAlignment="Center"
Background="Transparent">
<Button x:Name="btnEdit"
IsEnabled="{Binding IsButtonEnabled}"
Content="Add"
Margin="0, 0, 7, 0"
Width="35"
HorizontalAlignment="Right"
Command="{Binding EditCommand}"
CommandParameter="{Binding}"
IsTabStop="True" />
<ToolTipService.ToolTip>
<ToolTip Content="Ticket Required"
Visibility="{Binding IsButtonEnabled, Converter={StaticResource ReverseBoolToVisibilityConverter}}" />
</ToolTipService.ToolTip>
</Border>
<CheckBox HorizontalAlignment="Center"
VerticalAlignment="Center"
Margin="0 -50 0 0"
IsChecked="{Binding IsButtonEnabled, Mode=TwoWay}" Content="Is Enabled"></CheckBox>
</Grid>
</UserControl>
And the code-behind including the converter class:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Net;
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.Animation;
using System.Windows.Shapes;
namespace SilverlightApplication14
{
public partial class MainPage : UserControl, INotifyPropertyChanged
{
private bool _IsButtonEnabled;
public bool IsButtonEnabled
{
get { return _IsButtonEnabled; }
set
{
_IsButtonEnabled = value;
OnPropertyChanged("IsButtonEnabled");
}
}
public MainPage()
{
InitializeComponent();
IsButtonEnabled = true;
LayoutRoot.DataContext = this;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class ReverseBoolToVisibilityConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value != null && (bool)value)
{
return Visibility.Collapsed;
}
return Visibility.Visible;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
}
The reason its not working is Whenever you use command with button its enalble disable properties are drive by Command's CanExecute method.
Also because its method you can't straight away bind Command's CanExecute method. So you need some workaround to get value of CanExecute method into property and bind that property to Visibility of ToolTip.
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.
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.