|
Here is part of one I wrote for a custom control. It's not a one liner obviously because it's more complicated, but its setting the visibility based on *4* conditions .
<DataTemplate.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Value="1">
<Condition.Binding>
<MultiBinding Converter="{StaticResource HeaderArrowConverter}">
<Binding RelativeSource="{RelativeSource Mode=TemplatedParent}" />
<Binding Path="View.AllowsSorting" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=ctrls:ListViewEx}" />
<Binding Path="View.SortColumn" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=ctrls:ListViewEx}" />
<Binding Path="View.SortDirection" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=ctrls:ListViewEx}" />
</MultiBinding>
</Condition.Binding>
</Condition>
</MultiDataTrigger.Conditions>
<MultiDataTrigger.Setters>
<Setter TargetName="arrow" Property="Data" Value="{StaticResource HeaderArrowUp}" />
<Setter TargetName="arrow" Property="Visibility" Value="Visible" />
</MultiDataTrigger.Setters>
</MultiDataTrigger>
When the value is returned as 1 from the converter, that trigger executes. I haven't had a case where I have to handle a range of values in one trigger, but I have seen some solutions online for that. Instead of having just "1", you can have something like "> 1 && < 5" or however they specify it.
A simple "one liner":
<Trigger Property="SomeProp" Value="15">
<Setter Property="Visibility" Value="Collapsed" />
</Trigger>
modified 5-Feb-12 18:21pm.
|
|
|
|
|
Interesting, this is not an area I have spent any time exploring (obviously). While I see this as 1 possible (and in this case generic) solution, it is not a simple or straight forward solution. I put this in the bucket of complex but generic code and it *may* be used again so it would be the preferred solution.
I had in mind where the visible condition is based on the content of 1 or more fields, not bools but string or numeric. Something that is really specific to only that one view, I would still use code in the VM to evaluate the current record!
Never underestimate the power of human stupidity
RAH
|
|
|
|
|
Mycroft Holmes wrote: I had in mind where the visible condition is based on the content of 1 or more
fields, not bools but string or numeric. Something that is really specific to
only that one view, I would still use code in the VM to evaluate the current
record!
That is what I showed in my more complex example. The multi-converter takes in 4 values and returns a decision based on those 4 values. I only showed you a small portion of that trigger, but there are actually 5 cases "1", "2", "3", "4", "5".
The problem with your solution is that you may leave holes where you update one of the values used in the calculation but forget the OnPropertyChanged("VisibilityForState1"); call.
I have done it your way in the past, but found that I didn't like having to put OnPropertyChanged() calls for 4 or 5 properties all over the place.
Also, I kind of believe that the VM should strictly be for communication between the V and M. Adding a bunch of properties to support the view couples them more closely then they should be.
|
|
|
|
|
SledgeHammer01 wrote: The problem with your solution is that you may leave holes where you update one of the values used in the calculation but forget the OnPropertyChanged("VisibilityForState1"); call. Yeah that is one of the main drawbacks. I often throw all the conditional tests into 1 method and call that from multiple events. Which is basically what you are doing in xaml duh!
I can see some research into triggers are going to be needed!
Never underestimate the power of human stupidity
RAH
|
|
|
|
|
Mycroft Holmes wrote: Yeah that is one of the main drawbacks. I often throw all the conditional tests
into 1 method and call that from multiple events. Which is basically what you
are doing in xaml duh! I can see some research into triggers are
going to be needed!
One other thought I had about your approach while I was watching TV was that unless you **really** know the VM and its view bindings and how everything works together, you run the real risk of un-necessary / unintentional (and potentially expensive) screen updates.
By "throwing multiple updates in to one function" as you said, you could unintentionally trigger updates for say a few hundred properties when you just updated one. Maybe not on purpose , but by accident.
By doing this via triggers and self enforcing the rule that only the setter for PropertyA is allowed to put up a OnPropertyChanged("PropertyA") notification (and nothing else), you really mitigate this risk and optimize screen updates. All your setters should of course have "if (_theProp != value) { }" checks.
Now, with my trigger example where I bind to 4 properties in a multivalue converter, and I only change one of them, yeah, its going to call the converter function again, but the other 3 values are already cached by WPF. Also, there is very little overhead as the converter instance is cached as well, so you aren't recreating it.
Heck, you don't even need to do it that way. If your conditions are "basic", you can have a multiple condition trigger without the use of a converter at all.
The reason I had to use a converter there was because the logic that returned 1,2,3,4 or 5 was more complex then XAML allowed.
|
|
|
|
|
SledgeHammer01 wrote: By "throwing multiple updates in to one function" as you said,
No no no no -- multiple criteria for 1 update. Although the possibility does exists for your scenario and yeah it is a very valid argument for the trigger requirement.
Never underestimate the power of human stupidity
RAH
|
|
|
|
|
Sorry Andy we seemed to have hijacked your thread
Never underestimate the power of human stupidity
RAH
|
|
|
|
|
No need to apologise, I may just have learnt something!
I don't speak Idiot - please talk slowly and clearly
'This space for rent'
Driven to the arms of Heineken by the wife
|
|
|
|
|
For a TreeView I would like my items to contain an image and a string.
I have created a WPF UserControl for this called: TreeNode, which has this declaration C# and XAML codes:
public partial class TreeNode : UserControl
{
public TreeNode()
{
InitializeComponent();
}
public string Header
{
get { return txtNodeHeader.Text; }
set { txtNodeHeader.Text = value; }
}
public Image Image
{
get { return imgNodeImage; }
set { imgNodeImage = value; }
}
}
<UserControl x:Class="TreeViewPicture.TreeNode" 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="20"
d:DesignWidth="150">
<StackPanel Orientation="Horizontal">
<Image Name="imgNodeImage" Width="16" Height="16" />
<Separator Width="10" />
<TextBlock Name="txtNodeHeader" Text="NodeHeaderText" />
</StackPanel>
</UserControl>
For my MainWindow I have this XAML codes:
<Window x:Class="TreeViewPicture.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TreeViewPicture"
Title="MainWindow" Height="350" Width="525">
<Grid>
<TreeView Name="tvTree">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Nodes}" DataType="{x:Type local:ImageNode}">
<Grid>
<local:TreeNode Header="{Binding Header}" Image="{Binding Image}" />
</Grid>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Grid>
</Window>
MainWindow.cs contains a set of classes to define my tree structure:
Base node object for all items in the TreeView:
public abstract class Node
{
ObservableCollection<Node> nodes = new ObservableCollection<Node>();
Image image;
string header;
public Node(string header, BitmapImage image)
{
this.header = header;
this.image = new Image();
this.image.Width = 16;
this.image.Source = image;
}
public string Header
{
get { return header; }
}
public Image Image
{
get { return image; }
}
public ObservableCollection<Node> Nodes
{
get { return nodes; }
}
}
Top level object in tree structure
public class Server : Node
{
public Server(string header, BitmapImage image) : base (header, image)
{
}
}
All other levels in tree structure
public class Gateway : Node
{
public Gateway(string header, BitmapImage image) : base(header, image)
{
}
}
MainWindow class:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
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;
public partial class MainWindow : Window
{
BitmapImage serverImage;
BitmapImage gatewayImage;
public MainWindow()
{
InitializeComponent();
InitImages();
Server server = new Server("Server", serverImage);
Gateway gateway = new Gateway("Gateway", gatewayImage);
ObservableCollection<Node> nodes = new ObservableCollection<Node>();
nodes.Add(server);
server.Nodes.Add(gateway);
tvTree.ItemsSource = nodes;
}
private void InitImages()
{
serverImage = new BitmapImage();
serverImage.BeginInit();
serverImage.UriSource = new Uri(@"C:\Users\TheUser\Images\Server.png");
serverImage.EndInit();
gatewayImage = new BitmapImage();
gatewayImage.BeginInit();
gatewayImage.UriSource = new Uri(@"C:\Users\TheUser\Images\Gateway.png");
gatewayImage.EndInit();
}
}
When I try to run this I get a XamlParseException with the following text:
A 'Binding' cannot be set on the 'Header' property of type 'TreeNode'. A 'Binding' can only be set on a DependencyProperty of a DependencyObject.
XAML is rather tricky and I cannot make any sense of that and what I'm doing wrong.
If I swop in MainWindow XAML code to use the TextBlock object instead of TreeNode. I can see the items I want in the TreeView, but with no picture of course.
Any help would be very nice.
|
|
|
|
|
Solved the problem with this adjustment:
Node class:
public abstract class Node
{
ObservableCollection<Node> nodes = new ObservableCollection<Node>();
BitmapImage image;
string header;
public Node(string header, BitmapImage image)
{
this.header = header;
this.image = image;
}
public string Header
{
get { return header; }
}
public BitmapImage Image
{
get { return image; }
}
public ObservableCollection<Node> Nodes
{
get { return nodes; }
}
}
MainWindow XAML TreeView:
<TreeView Name="tvTree">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Nodes}" DataType="{x:Type local:Node}">
<StackPanel Orientation="Horizontal">
<Image Width="16" Height="16" Source="{Binding Image}" />
<Separator Width="10" Visibility="Hidden" />
<TextBlock Text="{Binding Header}" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
Of course is that the easiest solution *face palm*
|
|
|
|
|
I have been trying to understand how the GridSplitter works in WPF, so I have written a few tests using it in various environments. In that process I have noticed some odd behavior. Since my tests are using only binding and are written only with XAML I wonder if what I see is evidence of buggy code within WPF, or if I am doing something that should not be done.
I have a WPF application that opens a Window defined using this xaml, and only the default code behind:
<Window x:Class="GridSplitter.GridSplitterWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="GridSplitter"
Width="580"
Height="300">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Name="c0"
Width="*" />
<ColumnDefinition Name="c1"
Width="*" />
<ColumnDefinition Name="c2"
Width="*" />
<ColumnDefinition Name="c3"
Width="*" />
<ColumnDefinition Name="c4"
Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Name="col0"
Grid.Row="0"
Grid.Column="0"
Text="Col 0 Actual Width"
TextWrapping="Wrap" />
<TextBlock Grid.Row="1"
Grid.Column="0"
Text="{Binding ElementName=col0,
Path=ActualWidth,
StringFormat=\{0:F\}}" />
<TextBlock Name="col1"
Grid.Row="0"
Grid.Column="1"
Text="Col 1 Actual Width"
TextWrapping="Wrap" />
<TextBlock Grid.Row="1"
Grid.Column="1"
Text="{Binding ElementName=col1,
Path=ActualWidth,
StringFormat=\{0:F\}}" />
<TextBlock Name="col2"
Grid.Row="0"
Grid.Column="2"
Text="Col 2 Actual Width"
TextWrapping="Wrap" />
<TextBlock Grid.Row="1"
Grid.Column="2"
Text="{Binding ElementName=col2,
Path=ActualWidth,
StringFormat=\{0:F\}}" />
<TextBlock Name="col3"
Grid.Row="0"
Grid.Column="3"
Text="Col 3 Actual Width"
TextWrapping="Wrap" />
<TextBlock Grid.Row="1"
Grid.Column="3"
Text="{Binding ElementName=col3,
Path=ActualWidth,
StringFormat=\{0:F\}}" />
<TextBlock Name="col4"
Grid.Row="0"
Grid.Column="4"
Text="Col 4 Actual Width"
TextWrapping="Wrap" />
<TextBlock Grid.Row="1"
Grid.Column="4"
Text="{Binding ElementName=col4,
Path=ActualWidth,
StringFormat=\{0:F\}}" />
<TextBlock Grid.Row="2"
Grid.Column="0"
Text="Width"
TextWrapping="Wrap" />
<TextBox Grid.Row="3"
Grid.Column="0"
Text="{Binding Path=Width,
ElementName=c0,
Mode=TwoWay}" />
<TextBlock Grid.Row="2"
Grid.Column="1"
Text="Width"
TextWrapping="Wrap" />
<TextBox Grid.Row="3"
Grid.Column="1"
Text="{Binding Path=Width,
ElementName=c1,
Mode=TwoWay}" />
<TextBlock Grid.Row="2"
Grid.Column="2"
Text="Width"
TextWrapping="Wrap" />
<TextBox Grid.Row="3"
Grid.Column="2"
Text="{Binding Path=Width,
ElementName=c2,
Mode=TwoWay}" />
<TextBlock Grid.Row="2"
Grid.Column="3"
Text="Width"
TextWrapping="Wrap" />
<TextBox Grid.Row="3"
Grid.Column="3"
Text="{Binding Path=Width,
ElementName=c3,
Mode=TwoWay}" />
<TextBlock Grid.Row="2"
Grid.Column="4"
Text="Width"
TextWrapping="Wrap" />
<TextBox Grid.Row="3"
Grid.Column="4"
Text="{Binding Path=Width,
ElementName=c4,
Mode=TwoWay}" />
<GridSplitter Grid.RowSpan="4"
Grid.Column="2"
Width="4"
VerticalAlignment="Stretch" />
</Grid>
</Window>
The Binding does two things: (1) It tells me what is the ActualWidth of the columns. (I am actually binding to Label s that have their Width property set to "Auto" . When I tried to bind to the ActualWidth of the ColumnDefinition tags, I started seeing some of the behavior I'll describe below, so I thought I'd try this as a workaround.) (2) It allows me to change the Width property of the columns during run time.
When I compile and execute and use the GridSpliter , several things occur that I think should not. To cut to the chase I'll enumerate:
1. If you run the window just as I have written it, and then move the GridSplitter to the left, the third column collapses unexpectedly hiding most of its contents. (Strike this. I've just discovered why this happens, but I'll wait before I explain. It might be a nice puzzle to figure out.)
2. If all of the columns are given a Width of Auto , it is possible to push the two left columns beyond the right edge of the Window , and even beyond so that the GridSplitter could not be grabbed by the mouse, if it was dropped there.
3. If the two outer columns on both sides are given a Width of 100, and the middle column is given a Width of "*", it is possible to move the GridSplitter to the left of the middle column and this results in pushing the two columns on the right to the right, again right off the Window !
Item #1 I know is because of the Binding and the design of the window, so I would not consider it evidence of a bug in Microsoft's code. But what of #2 and #3? Is that a result of something I wrote in XAML, or is it due to poor validation in the Grid and GridSplitter code?
When I saw this I thought of CodePlex, and wondered if users here would be intrigued with this strange behavior. So, what do you think?
|
|
|
|
|
Well here is a bit more to my conundrum. I want to create divided panel with two areas for controls. I started with a Grid with three columns. I put a GridSplitter in the second column, and set the column's width to "Auto" so its Width is just the Width of the splitter.
I want the splitter to be able to resize the left and right columns, so I set the splitter's ResizeBehavior to PreviousAndNext . Also I want to be able to completely minimize the left column, but I want the right column always to be at least 200 pixels. For the Width property of the left column I set it to 130, and the Width property of the last column to * and its MinWidth property to 200.
When I run the window I can move the GridSplitter left, and it will move to the left side of the DockWindow , and stop there. When I move it to the right though, it will move off the screen pushing the whole column out of view.
Here is the XAML for it:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="130" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" MinWidth="200" />
</Grid.ColumnDefinitions>
<GridSplitter Grid.Column="1" Width="3" ResizeBehavior="PreviousAndNext" />
</Grid>
What can I do to enable the features I want, and to prevent the right column from completely leaving the Window?
|
|
|
|
|
Well, I'm not going to read "5 pages" of XAML in your previous post, so its good that you cut it down. Haha . You discovered a bug that I reported to Microsoft a year or two ago. I reported it in .NET 4.0. GridSplitter ignores the MinWidth on the last column in certain situations.
I invented a work-around at the time. Here you go. Just use local:GridSplitterEx (or whatever) instead of GridSplitter and you'll be off to the races:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace Shared
{
public class GridSplitterEx : GridSplitter
{
#region Constructors
public GridSplitterEx()
{
}
#endregion
#region Overrides
protected override void OnMouseMove(MouseEventArgs e)
{
if (IsMouseCaptureWithin)
{
Grid grid = Parent as Grid;
if (grid != null)
{
ColumnDefinition cd = grid.ColumnDefinitions[grid.ColumnDefinitions.Count - 1];
if (cd.MinWidth > 0)
{
double dWidth = grid.ActualWidth;
if (dWidth > 0)
{
Point ptGrid = e.GetPosition(grid);
if (dWidth - ptGrid.X < cd.MinWidth + 12)
return;
}
}
}
}
base.OnMouseMove(e);
}
#endregion
}
}
As a side note, I don't quite remember whats with the + 12 magic number, but there was a reason .
|
|
|
|
|
Thank you for the response.
I think the magic number is a fudge factor to allow your workaround to have a little room to see the mouse before the column actually gets to the MinWidth size. If it finally reaches that size, the column will stop shrinking, but the Grid starts to grow causing dWidth to get larger along with ptGrid.X .
Using binding I ran a test with GridSplitterEx to watch the size of the right column as the splitter moved right. The column would shrink to a size of about 210 pixels (close to MinWidth + 12 ) plus or minus a pixel or two (it wasn't always the same), when the splitter would stop moving.
So your code does workaround the problem for the simplified Window I presented, giving the approximate minimum size for the column, but it did not work unmodified, if I added another column between the splitter and the last column in my XAML.
The problem is not that the MinWidth is ignored, but that the right hand columns are being pushed to the right. Even if several columns are given fixed widths on the right of the splitter, as long as there is one column with a Width of "*" left of the splitter, it will push the right hand columns completely off the window.
Perhaps there is a setting to keep the Grid from growing beyond the edge of its container. If so, that might prevent this behavior.
|
|
|
|
|
Gil Yoder wrote: Using binding I ran a test with GridSplitterEx to watch the size of
the right column as the splitter moved right. The column would shrink to a size
of about 210 pixels (close to MinWidth + 12 ) plus or minus a pixel
or two (it wasn't always the same), when the splitter would stop moving.
Oh yeah, I now have some vague recollection about the grid "randomly" growing if I went past a certain point (the + 12). I don't remember the exact details, but that sounds like the right neighborhood.
Gil Yoder wrote: your code does workaround the problem for the simplified Window I
presented, giving the approximate minimum size for the column, but it did not
work unmodified, if I added another column between the splitter and the last
column in my XAML.
Correct. I only needed a left & right column, so GridSplitterEx only handles that case . Doing it generically for multiple columns on the right seemed like a lot of unnecessary work at the time .
It *is* a bug in .NET & GridSplitter though.
Unfortunately, it is not easy to fix. If you look at the source code for GridSplitter, everything you need access to (to do this right) is private. No virtual methods, etc. to override.
I think if you want to do a generic multi-column fix, you need to do the work in a GridEx : Grid type class and override MeasureOverride & ArrangeOverride and fix the bug there.
Basically copy the source for those two functions and debug to see where the bug is... but really, what is the expected behavior?
Lets say you have something like:
1 (min 200) | 2 (min 200) | 3 (min 200)
No matter how you fix it, it is always going to be possible to run 2 & 3 off the screen. For example... maximize the window, move splitter 1 all the way to the right. Then minimize. 2 and 3 will be gone.
There are a whole bunch of cases you need to handle .
Even my fix will not handle that case for just one splitter. Resizing the window horizontally will not resize the panes.
|
|
|
|
|
SledgeHammer01 wrote: Unfortunately, it is not easy to fix. If you look at the source code for GridSplitter, everything you need access to (to do this right) is private. No virtual methods, etc. to override.
I saw that. After my last note I looked at the code for GridSplitter trying to see what your override was changing, and thought I might look for another virtual method that might do the trick, but it sounds like you've done that already, so I'll save myself some time and look for another windmill to fight.
Thanks again!
|
|
|
|
|
I discovered a pretty easy way to keep from pushing the right hand columns beyond the visible window, and the technique should work for any number of columns. In a two column Grid instead of setting a minimum width on the right hand column, set a maximum width on the left column. If the Grid size is fixed, that is all that would be necessary. The width to set is the desired MinWidth subtracted from width of the Grid . When the left hand column is equal to the maximum size, the splitter won't move any further right.
If the Grid size needs to be elastic, a little coding should handle that so that the MaxWidth of the left column is always equal to Grid.Width minus the desire maximum width of the right hand column.
|
|
|
|
|
I don't think thats going to work. Say I have:
1 | 2 | 3
I set 1 to max 100, 2 to max 100 and 3 to max 100 under the assumption that my grid is 300 wide. At this point there is no way to push 2 and 3 out of the view. But if I maximize my window to 600 pixel wide, now the view will be all messed up since 1, 2 and 3 will only be 100 wide each and you'll have 300 of empty space. Like wise, if I shrunk the window to 250 px, I could push 3 halfway out of view by maximizing 1 and 2.
The "correct" approach as you will see in real production apps like Visual Studio, etc. is that the min and max are limited to proportional amounts of the width (i.e. 25%) and when the frame is resized, they resize all the panes proportionally.
If the grid is 300 wide and 1,2,3 are 100 each, if you resize to 250, you should probably take 16.6 px from each pane. If you resize to 350, you should probably add 16.6 px to each pane.
You should NEVER allow a pane to run off the screen no matter what happens.
|
|
|
|
|
Some time ago I asked here the question how to code adding/removing columns to a Listview at runtime and 'binding columns to viewmodels'
SeldgeHammer came with this solution:
1) The WPF ListView doesn't support data binding on the GridViewColumnCollection out of the box. You can add support for this, not too difficult, but a little bit of work.
2) Once you have a WPF ListView that can support data binding on the GridViewColumnCollection, its just a matter of having your VM return a collection of columns.
3) Some gotcha's you'll run into:
a) a GridViewColumn can only be owned by one GridViewColumnCollection at a time, so you can't return a GridViewColumnCollection from your VM, you need to return an ObservableCollection<gridviewcolumn>.
b) defining a GridViewColumn in code with bindings is a major PITA, so you should devise a way to load them out of a XAML file where you'll again run into "issue a" where you can't share GridViewColumn's.
All these issues are overcomeable... basically what I ended up with was a GridViewEx class that supported two-way binding on the column collection. Remember, you are going to need to save column widths and column order .
I wanted to share a blog on this topic that I ran into the other day. I quite like the solution provided there. Sharing info never hurts right?
http://blogs.msdn.com/b/nathannesbit/archive/2009/03/13/developing-reusable-controls-with-the-model-view-viewmodel-pattern.aspx[^]
|
|
|
|
|
Thanks but you could always consider posting this blog under the original question rather than here.
This isn't a question.
|
|
|
|
|
I doubt he is the author of the blog, so sharing in this manner is excellent, he got my 5
Never underestimate the power of human stupidity
RAH
|
|
|
|
|
I am facing problems with binding itemscontrol to a list of values. Below is my code in details:
Model code:
public class Employee
{
public string Name { get; set; }
public string Salary { get; set; }
}
public class Department
{
public string DepartmentName { get; set; }
public List<Employee> EmployeeDetails { get; set; }
}
View Model Code:
public ObservableCollection<Department> Departments { get; set; }
Departments = new ObservableCollection<Department>();
Departments.Add(new Department {DepartmentName = "TECH", EmployeeDetails = new List<Employee> {new Employee {Name = "XYZ", Salary = "10000.00" }}});
XAML:
<ItemsControl ItemsSource="{Binding Departments}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Expander Header="{Binding Path=DepartmentName}" Margin="12,12,12,0" VerticalAlignment="Top">
<Border BorderBrush="Black" BorderThickness="1" x:Name="border1">
<Grid Height="50">
<Grid.RowDefinitions>
<RowDefinition Height="25" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" HorizontalAlignment="Center" VerticalAlignment="Center" Text="{Binding EmployeeDetails.Name}" TextWrapping="Wrap" FontSize="10"/>
<TextBlock Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center" Text="{Binding EmployeeDetails.Salary}" TextWrapping="Wrap" FontSize="10"/>
</Grid>
</Border>
</Expander>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The data is not displaying with the code.
If the Department class is as below
public class Department
{
public string DepartmentName { get; set; }
public Employee EmployeeDetails { get; set; }
}
Then it works fine. But I need Department class should be as
public class Department
{
public string DepartmentName { get; set; }
public List<Employee> EmployeeDetails { get; set; }
}
And with this the data is not displaying. Please help me out.
Thanks in Advance,
|
|
|
|
|
You are going to have to put EmployeeDetails in another items container, as the way you have it now, it is not binding to the collection. So, instead of
<Grid Height="50">
<Grid.RowDefinitions>
<RowDefinition Height="25" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" HorizontalAlignment="Center"
VerticalAlignment="Center" Text="{Binding EmployeeDetails.Name}"
TextWrapping="Wrap" FontSize="10"/>
<TextBlock Grid.Column="1" HorizontalAlignment="Center"
VerticalAlignment="Center" Text="{Binding EmployeeDetails.Salary}"
TextWrapping="Wrap" FontSize="10"/>
</Grid>
you should have something like this
<ItemsControl ItemsSource="{Binding Path=EmployeeDetails}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" HorizontalAlignment="Center"
VerticalAlignment="Center" Text="{Binding Name}"
TextWrapping="Wrap" FontSize="10" Margin="5"/>
<TextBlock Grid.Column="1" HorizontalAlignment="Center"
VerticalAlignment="Center" Text="{Binding Salary}"
TextWrapping="Wrap" FontSize="10" Margin="5" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
When I was a coder, we worked on algorithms. Today, we memorize APIs for countless libraries — those libraries have the algorithms - Eric Allman
modified 31-Jan-12 6:23am.
|
|
|
|
|
Thanks Eric Allman.
It works now.
|
|
|
|
|
Glad to help. My Name is Wayne Gaylard - Eric Allman is an author I quote in my signature.
When I was a coder, we worked on algorithms. Today, we memorize APIs for countless libraries — those libraries have the algorithms - Eric Allman
|
|
|
|
|