I am using the sample application provided along with the Gong solutions drag drop library.
The solution includes a listbox having itemssource and displaymemberpath set.
I have modified the application to include an itemscontrol and itemTemplate.
But the solution no longer works. There is an exception in the DragInfo.cs file.
Not sure if posting it here is correct.
But can someone help me with this. The sample code is pretty basic.
<ItemsControl
dragDropFramework:DragDrop.IsDragSource="True"
Grid.Column="0" ItemsSource="{Binding Pupils}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Label Content="{Binding FullName}" BorderBrush="Brown" BorderThickness="2" Margin="2"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ItemsControl ItemsSource="{Binding Schools}"
dragDropFramework:DragDrop.DropHandler="{Binding}"
dragDropFramework:DragDrop.IsDropTarget="True"
Grid.Column="1">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Label Content="{Binding Name}" BorderBrush="Brown" BorderThickness="2" Margin="2"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
class PupilViewModel
{
public string FullName { get; set; }
}
internal class WindowViewModel : IDropTarget
{
public ICollectionView Schools { get; private set; }
public ICollectionView Pupils { get; private set; }
public SideWindowViewModel()
{
var pupils = new ObservableCollection<PupilViewModel>
{
new PupilViewModel { FullName = "Alex Thompson" },
new PupilViewModel { FullName = "Tabitha Smith" },
new PupilViewModel { FullName = "Carl Pederson" },
new PupilViewModel { FullName = "Sarah Jones" },
new PupilViewModel { FullName = "Paul Lowcroft" }
};
this.Pupils = CollectionViewSource.GetDefaultView(pupils);
var schools = new SchoolViewModel { Name = "FirstSchool", Pupils = new ObservableCollection<PupilViewModel>() };
this.Schools = CollectionViewSource.GetDefaultView(schools);
}
public void DragOver(DropInfo dropInfo)
{
if (dropInfo.Data is PupilViewModel)// && dropInfo.TargetItem is SchoolViewModel)
{
dropInfo.DropTargetAdorner = DropTargetAdorners.Highlight;
dropInfo.Effects = DragDropEffects.Move;
}
}
public void Drop(DropInfo dropInfo)
{
throw new NotImplementedException();
}
The Window's dataContext, is set to an instance of WindowViewModel.
This code comes with Gong library also as a part of code project.
http://www.codeproject.com/Articles/43614/Drag-and-Drop-in-WPF
original code looks like this
<ListBox Grid.Column="1" ItemsSource="{Binding Schools.CurrentItem.Pupils}" DisplayMemberPath="FullName"
dd:DragDrop.IsDragSource="True" dd:DragDrop.IsDropTarget="True"/>
In case you haven't figured this one out yet - it looks like you commented out where it checks to see if the thing you are dragging over is a SchoolView. Since you are using DropTargetAdorners.Highlight it is trying to highlight what you are dragging over. Since there isn't anything you're getting the null reference error. So maybe go back to this?
public void DragOver(DropInfo dropInfo)
{
if (dropInfo.Data is PupilViewModel) && dropInfo.TargetItem is SchoolViewModel)
{
dropInfo.DropTargetAdorner = DropTargetAdorners.Highlight;
dropInfo.Effects = DragDropEffects.Move;
}
}
Related
I have the following XAML containing a ListBox bound to an ObservableCollection
<ListBox
Margin="0,5"
Grid.Row="1"
Grid.ColumnSpan="3"
Visibility="{Binding ArePicturesAvailable, Converter={StaticResource BoolToVisConv}}"
SelectedItem="{Binding SelectedPicture}"
ItemsSource="{Binding Pictures, NotifyOnSourceUpdated=True}"
ScrollViewer.VerticalScrollBarVisibility="Disabled">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Image Margin="8"
Height="{Binding Size.Height}"
Width="{Binding Size.Width}"
Source="{Binding FullPath, Converter={StaticResource RelativeToFullConv}}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Since there is noticeable delay in populating the ListBox, I am attempting to defer this until the ListBox's containing view has loaded using Dispatcher.BeginInvoke as illustrated below:
public class PictureMarkerEditorViewModel
{
private PictureSelectorViewModel _pictureSelectorViewModel;
public string PhotoCollectionDirectory { get; set; }
protected ISymbolEditor GetEditorImpl(ISymbolInfo symbolInfo)
{
var picSymbolInfo = (PictureMarkerSymbolInfo)symbolInfo;
_pictureSelectorViewModel = new PictureSelectorViewModel(picSymbolInfo);
var editor = new PictureMarkerSymbolEditor(_pictureSelectorViewModel, picSymbolInfo);
editor.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() =>
{
var photos = GetPhotoCollection();
_pictureSelectorViewModel.Pictures.AddRange(photos);
}));
//var photos = GetPhotoCollection();
//_pictureSelectorViewModel.Pictures.AddRange(photos);
return editor;
}
public IEnumerable<Picture> GetPhotoCollection()
{
if (string.IsNullOrEmpty(PhotoCollectionDirectory))
return null;
if (!Directory.Exists(PhotoCollectionDirectory))
throw new ArgumentException("Cannot show images from invalid directory " + PhotoCollectionDirectory + ".");
var files = Directory.GetFiles(PhotoCollectionDirectory, "*.png", SearchOption.AllDirectories);
return files.Select(f => new Picture(Path.GetFileName(f), f, ImageUtils.GetDimensions(f)));
}
}
where PictureSelectorViewModel.Pictures is simply an ObservableCollection of Picture:
public class PictureSelectorViewModel
{
private readonly ObservableCollection<Picture> _pictures= new ObservableCollection<Picture>();
public ObservableCollection<Picture> Pictures
{
get
{
return _pictures;
}
}
}
and Picture is a simple class containing Name and FullPath properties:
public class Picture
{
public string Name { get;set}
public string FullPath {get; set;}
public Size Size {get;set;}
}
I simply cannot get the list populated after it has loaded. If I populate the Pictures collection before the view is loaded (the commented part of the code, I see the images.
Any one know why?
TIA.
I am very new to WPF, and help appreciated:
Model:
Collection<Presenter>,
Presenter has a Collection<Presentation>,
Presentation has a TeachingSession property (which contains a DateTime? property)
I am trying to have a treeview display:
presenter name
[combobox of available Dates]
At the moment, each presenter name in the treeview is displaying correctly, and the first parent item expanded displays the combobox with the correctly selected date. However, comboboxes displaying at any one time are all 'in sync' - that is changing the value in a combobox (or expanding a different treeview item) changes the value for all comboboxes which can be seen, so they all display the same date.
<TreeView Name="PresenterTreeView" ItemsSource="{Binding}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:Presenter}"
ItemsSource="{Binding Path=Presentations}">
<TextBlock Text="{Binding Path=FullName}" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:Presentation}">
<ComboBox SelectedItem="{Binding Path=TeachingSession, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
DisplayMemberPath="SessionDate"
ItemsSource="{Binding Source={StaticResource availableDatesViewSource}}" >
</ComboBox>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
I have compliled your example and I cant get the comboboxes to sync like you are describing.
Perhaps you will be able to see what I done different and it might be the fix, Or maybe I am just wrong and am missing somthing from your question?
This is the code I used:
Xaml:
<Window x:Class="WpfApplication8.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="340" Width="480" Name="UI" xmlns:local="clr-namespace:WpfApplication8">
<TreeView Name="PresenterTreeView" DataContext="{Binding ElementName=UI}" ItemsSource="{Binding Path=Presenters}" >
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:Presenter}" ItemsSource="{Binding Path=Presentations}">
<TextBlock Text="{Binding FullName}" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:Presentation}">
<ComboBox SelectedItem="{Binding TeachingSession.SessionDate}" ItemsSource="{Binding ElementName=UI, Path=Dates}" />
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
</Window>
Code:
public partial class MainWindow : Window
{
private ObservableCollection<Presenter> _myProperty = new ObservableCollection<Presenter>();
private ObservableCollection<DateTime?> _myDates = new ObservableCollection<DateTime?>();
public MainWindow()
{
InitializeComponent();
DateTime time1 = DateTime.Now;
DateTime time2 = DateTime.Now.AddDays(1);
Dates.Add(time1);
Dates.Add(time2);
for (int i = 0; i < 20; i++)
{
Dates.Add(DateTime.Now.AddDays(i));
}
TeachingSession teach = new TeachingSession { SessionDate = time1 };
Presentation pres = new Presentation { TeachingSession = teach };
Presenter presenter = new Presenter { FullName = "Presenter1" };
presenter.Presentations = new ObservableCollection<Presentation>();
presenter.Presentations.Add(pres);
TeachingSession teach1 = new TeachingSession { SessionDate = time2 };
Presentation pres1 = new Presentation { TeachingSession = teach1 };
Presenter presenter1 = new Presenter { FullName = "Presenter1" };
presenter1.Presentations = new ObservableCollection<Presentation>();
presenter1.Presentations.Add(pres1);
Presenters.Add(presenter);
Presenters.Add(presenter1);
}
public ObservableCollection<Presenter> Presenters
{
get { return _myProperty; }
set { _myProperty = value; }
}
public ObservableCollection<DateTime?> Dates
{
get { return _myDates; }
set { _myDates = value; }
}
}
public class Presenter
{
public string FullName { get; set; }
public ObservableCollection<Presentation> Presentations { get; set; }
}
public class Presentation
{
public TeachingSession TeachingSession { get; set; }
}
public class TeachingSession
{
public DateTime? SessionDate { get; set; }
}
Result:
Because of sa_ddam213's answer, i was able to track the problem down to the default synchronisation with the CollectionViewSource used to bindItemsSource
The combobox needed the attribute:
IsSynchronizedWithCurrentItem="False"
I've got the following ItemsControl that gives me a check box for every database within the available collection. These checkboxes allow the user to select which ones to filter on. The databases to filter on are in a separate collection (FilteredDatabases). How exactly do I do this? I could add an InFilter property to the database item class. But, I don't want to start changing this code yet. The problem I can't get around in my head is the fact that I need to bind to a property that is not on the database item itself. Any ideas?
<ItemsControl ItemsSource="{Binding AvailableDatabases}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Name}" IsChecked="{Binding ???}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
// In view model
public IBindingList FilteredDatabases
{
get;
private set;
}
public IBindingList AvailableDatabases
{
get;
private set;
}
Bind CheckBox.Command to routed command instance
Bind routed command to method
Use IBindingList.Add and IBindingList.Remove methods
The following code illustrates what you are trying to do, in order to do this you are better off using ObservableCollection instead of as your collection object, if an ItemsControl is bound to it it will automatically update the UI when viewmodels are added and removed.
XAML:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ItemsControl Grid.Column="0" ItemsSource="{Binding AvailableDatabases}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Name}" IsChecked="{Binding IsChecked}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ItemsControl Grid.Column="1" ItemsSource="{Binding FilteredDatabases}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
View Models:
public class MainViewModel
{
private ObservableCollection<DBViewModel> _availableDatabases;
private ObservableCollection<DBViewModel> _filteredDatabases;
public ObservableCollection<DBViewModel> AvailableDatabases
{
get
{
if (_availableDatabases == null)
{
_availableDatabases = new ObservableCollection<DBViewModel>(new List<DBViewModel>()
{
new DBViewModel(this) { Name = "DB1" , IsChecked = true},
new DBViewModel(this) { Name = "DB2" },
new DBViewModel(this) { Name = "DB3" },
new DBViewModel(this) { Name = "DB4" },
new DBViewModel(this) { Name = "DB5" },
new DBViewModel(this) { Name = "DB6" },
new DBViewModel(this) { Name = "DB7" , IsChecked = true },
});
}
return this._availableDatabases;
}
}
public ObservableCollection<DBViewModel> FilteredDatabases
{
get
{
if (_filteredDatabases == null)
_filteredDatabases = new ObservableCollection<DBViewModel>(new List<DBViewModel>());
return this._filteredDatabases;
}
}
}
public class DBViewModel
{
private MainViewModel _parentVM;
private bool _isChecked;
public string Name { get; set; }
public DBViewModel(MainViewModel _parentVM)
{
this._parentVM = _parentVM;
}
public bool IsChecked
{
get
{
return this._isChecked;
}
set
{
//This is called when checkbox state is changed
this._isChecked = value;
//Add or remove from collection on parent VM, perform sorting here
if (this.IsChecked)
_parentVM.FilteredDatabases.Add(this);
else
_parentVM.FilteredDatabases.Remove(this);
}
}
}
View models should also implement INotifyPropertyChanged, I omitted it since it was not necessary in this particular case.
I have 2 list boxes (ListA and ListB) that display data from different entities (EntityA, EntityB). These entities are related - EntityA has a property of a collection of EntityB. I want to be able to use drag and drop behaviour in order to add items from ListB into the collection of the dropped item in ListA.
To clarify, I don't want to add ListItemB into the collection of the selected ListItemA, I want to add it into the collection of the list item that I drop it onto (the ListItemA that the mouse is over when I release).
Using ListBoxDragDropTarget, is it possible for a ListBoxItem to be the drop target, instead of the listbox itself?
Any suggestions as to a solution for this scenario?
It can be done by creating two ListBoxes, as you described, one bound to an ObservableCollection<EntityA> and one bound to an ObservableCollection<EntityB>. EntityA contains an ObservableCollection<EntityB> as a property. The ListBox items of EntityA are templated to display the child collection of EntityB's as a ListBox. The ListBoxDragDropTarget is specified in this ItemTemplate, rather than the parent. Here is some XAML to demonstrate:
<ListBox Name="listOfEntityA">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding EntityName}" />
<toolKit:ListBoxDragDropTarget AllowDrop="True" AllowedSourceEffects="All">
<ListBox ItemsSource="{Binding ChildEntityBs}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding EntityName}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</toolKit:ListBoxDragDropTarget>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<toolKit:ListBoxDragDropTarget AllowDrop="True" AllowedSourceEffects="All">
<ListBox Name="listOfEntityB" />
</toolKit:ListBoxDragDropTarget>
After a bit of work I think I have it:
<StackPanel Orientation="Horizontal">
<Controls:ListBoxDragDropTarget AllowDrop="True">
<ListBox x:Name="FromBox" Width="200" ItemsSource="{Binding IssueList}" DisplayMemberPath="Name"/>
</Controls:ListBoxDragDropTarget>
<Controls:ListBoxDragDropTarget AllowDrop="True" Drop="ToBoxDragDropTarget_Drop">
<ListBox x:Name="ToBox" Width="150" ItemsSource="{Binding ObjectiveList}" DisplayMemberPath="Name" Margin="80,0,0,0" />
</Controls:ListBoxDragDropTarget>
<TextBlock x:Name="UpdateText"/>
</StackPanel>
and the codebehind (which will now be refactored into my ViewModel):
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
IssueList = new ObservableCollection<Issue>
{
new Issue{ ID = 1, Name="One"},
new Issue{ ID = 2, Name="Two"},
new Issue{ ID = 3, Name="Three"},
new Issue{ ID = 4, Name="Four"},
new Issue{ ID = 5, Name="Five"},
};
ObjectiveList = new ObservableCollection<Objective>
{
new Objective {ID = 10, Name = "Ten"},
new Objective {ID = 11, Name = "Eleven"},
new Objective {ID = 12, Name = "Twelve"},
new Objective {ID = 13, Name = "Thirteen"},
new Objective {ID = 14, Name = "Fourteen"},
};
LayoutRoot.DataContext = this;
}
public ObservableCollection<Issue> IssueList { get; set; }
public ObservableCollection<Objective> ObjectiveList { get; set; }
private void ToBoxDragDropTarget_Drop(object sender, Microsoft.Windows.DragEventArgs e)
{
var droppedOnObjective = ((FrameworkElement)e.OriginalSource).DataContext as Objective;
var args = e.Data.GetData(typeof(ItemDragEventArgs)) as ItemDragEventArgs;
if (args != null)
{
var draggedItems = args.Data as SelectionCollection;
var draggedItem = draggedItems[0];
if (droppedOnObjective != null)
{
var draggedIssue = (Issue)draggedItem.Item;
if (!droppedOnObjective.Issues.Contains(draggedIssue))
{
droppedOnObjective.Issues.Add(draggedIssue);
UpdateText.Text = string.Format("Issue <{0}> added to Objective <{1}>", draggedIssue.Name, droppedOnObjective.Name);
}
else
{
UpdateText.Text = string.Format("Objective <{0}> already contains Issue <{1}>", droppedOnObjective.Name, draggedIssue.Name);
}
}
else
UpdateText.Text = "selections or dropOnObjective is null";
}
else
UpdateText.Text = "args null";
}
}
public class Issue
{
public int ID { get; set; }
public string Name { get; set; }
}
public class Objective
{
public int ID { get; set; }
public string Name { get; set; }
public List<Issue> Issues { get; set; }
public Objective()
{
Issues = new List<Issue>();
}
}
I have a class ShipmentsCollection that inherits ObservableCollection which contains shipment objects (entities). This is displayed in a listbox on my ShipmentsView UserControl. My intent is to allow a user to type into a textbox above the list and filter the list with items that contain that string, as well as filter based on several checkbox and radiobutton based options (Delivery status and orderby direction).
I have tried this several ways, but none seem very elegant or really functional. Things I have tried follows:
Put ShipmentsCollection into a CollectionViewSource and filtered via predicate. Could not figure out a good way to make the filter auto update based on user typing or option change.
Refactored as a Class that Inherits collectionViewSource and tried to declare directly in XAML but got the following error: "The specified named connection is either not found in the configuration, not intended to be used with the EntityClient provider, or not valid". Tried fixing but could not find solution that worked.
Refactored to inherit from CollectionView, implemented filter logic, in event handler in codebehind. Still trying to figure out how I can get the filter string to the event handler without naming the filtertext textbox control.
Anyone got some good ideas in regard to implementing this functionality in an MVVM design pattern. I expect to have at most 200 objects in the list, so it will not be an enormous filter operation.
Cory
Your first option would be the one I would suggest. To get the auto-filter to work based on typing, I'd do something like a SearchString property in my ViewModel, bind the textbox text to that, and set the UpdateSourceTrigger in the binding to PropertyChanged so it will call the SearchString PropertyChanged event every time a key is typed instead of waiting until the box loses focus.
XAML:
<TextBox Text="{Binding SearchString, UpdateSourceTrigger=PropertyChanged}" />
ViewModel: With the above property set to PropertyChanged, the "Set" method gets called anytime a key is typed instead of just when the textbox loses focus.
private string _searchString;
public string SearchString
{
get { return _searchString; }
set
{
if (_searchString != value)
{
_searchString = value;
OnPropertyChanged("SearchString");
}
}
}
I know this question is closed and old. but for someone like me searching for dynamic filtering, can refer to the following link
https://github.com/lokeshlal/WPFDynamicFilters
the above example creates filters for each entity based on the attribute defined on property of entity model.
As an example:
Define an attribute for filters
public class FilterAttribute : Attribute
{
public FilterAttribute() { }
public string FilterLabel { get; set; }
public object FilterValue { get; set; }
public string FilterKey { get; set; }
public Type FilterDataType { get; set; }
public bool IsDropDown { get; set; }
public string DropDownList { get; set; }
public List<object> ObjectDropDownList { get; set; }
}
Apply the above attribute in model properties
public class GridModel
{
[Filter(FilterLabel = "Id",
FilterKey = "Id",
IsDropDown = false,
FilterDataType = typeof(int))]
public int Id { get; set; }
[Filter(FilterLabel = "Name",
FilterKey = "Name",
IsDropDown = false,
FilterDataType = typeof(string))]
public string Name { get; set; }
[Filter(FilterLabel = "Country",
FilterKey = "Country",
IsDropDown = true,
FilterDataType = typeof(int),
DropDownList = "Country")]
public string Country { get; set; }
[Filter(FilterLabel = "Address",
FilterKey = "Address",
IsDropDown = false,
FilterDataType = typeof(string))]
public string Address { get; set; }
}
Define the model that will bind to the drop down type
public class Country
{
public int Id { get; set; } // id will be used for value
public string Name { get; set; } // Name will be used for display value
}
ViewModel of actual View
public class FilterViewModel
{
public ICommand CheckFiltersCommand { get; set; }
public FilterViewModel()
{
CheckFiltersCommand = new DelegateCommand(GetFilters);
GridSource = new List<GridModel>();
GridSource.Add(new GridModel() { Id = 1, Name = "Name1", Country = "Denmark" });
GridSource.Add(new GridModel() { Id = 2, Name = "Name2", Country = "India" });
GridSource.Add(new GridModel() { Id = 3, Name = "Name3", Country = "Australia" });
GridSource.Add(new GridModel() { Id = 4, Name = "Name4", Country = "India" });
GridSource.Add(new GridModel() { Id = 5, Name = "Name5", Country = "Australia" });
GridSource.Add(new GridModel() { Id = 6, Name = "Name6", Country = "Hongkong" });
FilterControlViewModel = new FilterControlViewModel();
FilterControlViewModel.FilterDetails = new List<FilterAttribute>();
foreach (var property in typeof(GridModel).GetProperties())
{
if (property.GetCustomAttributes(true).Where(attr => attr.GetType() == typeof(FilterAttribute)).Any())
{
var attribute = (FilterAttribute)property.GetCustomAttributes(true).Where(attr => attr.GetType() == typeof(FilterAttribute)).First();
FilterControlViewModel.FilterDetails.Add(attribute);
}
}
}
private void GetFilters()
{
FilterCollection = new Dictionary<string, object>();
foreach (var filter in FilterControlViewModel.FilterDetails)
{
if (filter.IsDropDown)
{
if (filter.FilterValue != null)
FilterCollection.Add(filter.FilterKey, filter.FilterValue.GetType().GetProperty("Id").GetValue(filter.FilterValue));
}
else
{
FilterCollection.Add(filter.FilterKey, filter.FilterValue);
}
}
MessageBox.Show(string.Join(", ", FilterCollection.Select(m => m.Key + ":" + Convert.ToString(m.Value)).ToArray()));
}
public List<GridModel> GridSource { get; set; }
public Dictionary<string, object> FilterCollection { get; set; }
public FilterControlViewModel FilterControlViewModel { get; set; }
}
In the above view model 'FilterControlViewModel' property will iterate all property of model and collect the filter information of the properties.
This same property will be assigned to the user control as explained in xaml file below
<Window x:Class="WPFDynamicFilters.GridWithFilters"
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:WPFDynamicFilters"
mc:Ignorable="d"
Title="gridwithfilters" Height="481.239" Width="858.171">
<Grid>
<Grid HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" x:Name="FilterGrid" Height="209" Width="830">
<Border BorderThickness="1" BorderBrush="Gold"/>
<local:Filter DataContext="{Binding FilterControlViewModel, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Margin="0,0,0,10" />
</Grid>
<DataGrid x:Name="DataGrid" ItemsSource="{Binding GridSource}" HorizontalAlignment="Left" Margin="10,294,0,0" VerticalAlignment="Top" Height="146" Width="830"/>
<Button x:Name="button" Content="Check Filters" HorizontalAlignment="Left" Margin="10,245,0,0" VerticalAlignment="Top" Width="110" Command="{Binding CheckFiltersCommand}"/>
</Grid>
</Window>
Filter control will take all the attributes and render the control using itemscontrol
<UserControl x:Class="WPFDynamicFilters.Filter"
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:WPFDynamicFilters"
mc:Ignorable="d"
d:DesignHeight="40"
>
<UserControl.Resources>
<DataTemplate x:Key="TStringTemplate">
<StackPanel FlowDirection="LeftToRight">
<TextBlock Text="{Binding FilterKey}" />
<TextBox x:Name="TxtFieldValue"
Text="{Binding FilterValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
RenderTransformOrigin="-9.3,0.5" Width="200" FontSize="16" TextAlignment="Left" VerticalAlignment="Center"/>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="TIntegerTemplate">
<StackPanel FlowDirection="LeftToRight">
<TextBlock Text="{Binding FilterKey}" />
<TextBox x:Name="IntFieldValue"
Text="{Binding FilterValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
RenderTransformOrigin="-9.3,0.5" Width="200" FontSize="16" TextAlignment="Left" VerticalAlignment="Center"/>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="TDropDownTemplate">
<StackPanel FlowDirection="LeftToRight">
<TextBlock Text="{Binding FilterKey}" />
<ComboBox
SelectedItem="{Binding FilterValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectedIndex="{Binding FilterValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
ItemsSource="{Binding ObjectDropDownList, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
DisplayMemberPath="Name"
RenderTransformOrigin="-9.3,0.5" Width="200" FontSize="16" />
</StackPanel>
</DataTemplate>
<local:FilterTemplateSelector x:Key="FilterTemplateSelector"
StringTemplate="{StaticResource TStringTemplate}"
IntegerTemplate="{StaticResource TIntegerTemplate}"
DropDownTemplate="{StaticResource TDropDownTemplate}"
/>
</UserControl.Resources>
<Grid>
<ItemsControl ItemsSource="{Binding FilterDetails}" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="3" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentControl
Content="{Binding}"
HorizontalAlignment="Left"
ContentTemplateSelector="{StaticResource FilterTemplateSelector}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</UserControl>
Finally define the template selector
public class FilterTemplateSelector : DataTemplateSelector
{
public DataTemplate StringTemplate { get; set; }
public DataTemplate IntegerTemplate { get; set; }
public DataTemplate DropDownTemplate { get; set; }
public override DataTemplate SelectTemplate(object item,
DependencyObject container)
{
var filter = (item as FilterAttribute);
if (filter == null) return StringTemplate;
if (!filter.IsDropDown)
{
switch (filter.FilterDataType.Name.ToLower())
{
case "int32":
case "int64":
return IntegerTemplate;
case "string":
return StringTemplate;
}
}
else
{
// display drop down
switch (filter.DropDownList)
{
case "Country":
filter.ObjectDropDownList = GetDropDown.GetCountries().ToList<object>();
break;
}
return DropDownTemplate;
}
return StringTemplate;
}
}