Databind a value to a gridviewcolumn header? - wpf

Is it possible?
I have a listview with several gridviewcolumns. The last column has a dynamic header. I dont know what the column header will be at design time. It's actually a number I want to display as a string.
<GridViewColumn Header="{Binding Path=SomeValue}"
DisplayMemberBinding="{Binding Path=OtherValue}"/>
This doesn't seem to work. The data will bind fine just the header remains blank. Stepping through the code and it doesn't even break on the SomeValue property.

I think your problem is the source of the "SomeValue" property. If you are binding to a list of objects, it wouldn't make sense to have the header determined by a property on that object, because then you could have a different header for every object. Essentially what you are saying is "Bind the header of the column to the 'SomeValue' property which lives on the same object that my 'OtherValue' property does." The "SomeValue" needs to come from a different source other than the list your grid item is bound to. You need to either set the "RelativeSource" or the "ElementName" property in the binding.

read a post
http://blogs.infragistics.com/blogs/josh_smith/archive/2008/06/26/data-binding-the-isvisible-property-of-contextualtabgroup.aspx
2.create a datacontext spy class accourding to the post in step 1 (copy and paste it, it's a couple of lines)
3.usage the datacontext spy
<common:DataContextSpy x:Key="dci" DataContext="{Binding SomeProperty}" />
<DataGridTemplateColumn Header="{Binding Source={StaticResource dci},
Path=DataContext.SomePropertysListOfValues[14]}">

Related

WPF: How to bind to only one item in a collection, not using ItemsControl since I don't want to display all of them

I have this requirement, that I have a collection of items (ObservableCollection), but I only want to display the first item. The requirement comes from the fact that in most of the case, the collection only contains one item. And due to the space limit, even if there is more than one items in the collection, we'd like to display the number of the items, details of the first one (same presentation as prior situation) and a ... symbol to indicate to the user that there is more items. And when the mouse is over the UI element a popup will eventually display all items.
The first solution I can think of (please suggest others if they are better) is to bind to this collection (but not using an ItemsControl) and define a DataTemplateSelector derived class (which return either the DataTemplate to display the only one item, or the DateTemplate which has the ... and the popup for more details, based on the number of items in the collection) and use it as ContentTemplateSelector.
But now my question: how both of my DataTemplate would look like in XAML, so that they can display only the first item in the collection? Obviously I can't have a ItemsControl.
UPDATE:
Now I have managed to make it work and agree this question can be closed (I can't delete it anymore since there is already some answers).
I actually knew how to bind to one certain item in the collection, but this was not where I am confused. I felt I should use ContentControl as one answer suggests. But I thought since I need to bind to the whole collection (not to single indexed item), and use a DataTemplateSelector to select the proper DataTemplate based on the number of items in the collection. The code would look like this:
<ContentControl Content="{Binding MyCollection}"
ContentTemplateSelector="{StaticResource MyTemplateSelector}" />
And in MyTemplateSelector I wasn't sure how to use it since there is no reference to my collection because it is defined as resource and it doesn't have the information of MyCollection. However, it turned out to be very simple, the DataTemplate can refer to an indexed item without knowing the name or any other reference. Simply like this:
<DataTemplate>
<TextBlock Text="{Binding [0].PropertyName}" />
<DataTemplate />
To bind to just one item from a collection, you can use the following syntax:
{Binding Items[0]}
Or to bind to a property of a single item from the collection:
{Binding Items[0].Property}
You can find out more about property path syntax from the Binding.Path Property page at MSDN... from the linked page:
• Indexers of a property can be specified within square brackets following the property name where the indexer is applied. For instance, the clause Path=ShoppingCart[0] sets the binding to the index that corresponds to how your property's internal indexing handles the literal string "0". Multiple indexers are also supported.
Try this
<ContentControl Content="{Binding YourCollection[0]}">
<ContentControl.ContentTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"></TextBlock>
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
Ok, late to the party but I thought I'd share my 2 cents anyway: I'd better go with a dumber (XAML-)view and a view-model closer to your presentation needs.
Translated: instead of mapping your existing view-model (or raw data) and its collection of items directly to the view, I suggest to map that to an appropriate view-model showing something like a YourItemViewModel FirstItem property and a bool HasMore property. That second view-model would be easily unit-testable to make sure it behaves propertly, and would be easily mapped to a view with less logic, so to avoid possible hard-to-test problems in view.
{Binding Items[0].SomeProperty}
{Binding [0].SomeProperty}
{Path=/SomeProperty}

WPF - expose binding methods for inherited column

A reoccurring issue I have is needing to create enhanced text columns for datagrids. By that I mean columns that act just like normal text columns, but with an additional graphic or feature, like an image displayed next to the text. So I'm using template columns, but apparently this means having to "start from scratch" in generating a lot of the features expected of a normal text column, such as the textbox editing template:
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox
FocusManager.FocusedElement="{Binding RelativeSource={RelativeSource Self}}"
Text="{Binding Path=[binded text], Mode=TwoWay, UpdateSourceTrigger=LostFocus}"/>
I want to define a column that's inherited from DataGridTemplateColumn, then dump all this code into it, so I can reuse these columns with any datagrid I wish. But as shown above, I can't declare the binding in the class definition because that obviously depends upon usage.
How can I define an inherited datagrid column that makes use of child controls (specifically the cell editing textbox in this case), but still allows binding to be set for these controls when the column has been declared with xaml inside some actual datagrid?
So far I've tried to expose a method to do this, but it's not working:
Public Class MyTextColumn
Inherits DataGridTemplateColumn
....
Public Property EditorBinding As String
Get....
Set(value As String)
Dim b As New Binding(value)
b.Mode = BindingMode.TwoWay
b.UpdateSourceTrigger = UpdateSourceTrigger.LostFocus
Dim tb = DirectCast(Me.CellEditingTemplate.LoadContent, TextBox)
tb.SetBinding(TextBox.TextProperty, b)
End Set
End Property
Not working, my best guess is I'm not setting the Binding.Source, but I have no idea what I should be setting it to. It's getting pretty frustrating.
So if I understand you correctly, you want to be able to bind the text property of the TextBox to something on the parent control which will hold this child control of yours. You can't do that using the normal property (I'm guessing you got the "Can't bind because it's not the dependency property" exception or something similar).
This is how I usually do it without any problems. First you need to define a dependency property in the code behind. This should show you how to do it in the VB.net (I really really suck at VB.net so I won't pretend to give you any advice on that). Check the first example in VB.net. What you need to change first is from Boolean to String, you will also probably want to change the property name. Be careful to leave the "Property" part of the name where it stands in the example. GetType(MyCode) should be changed to the name of the class where you are implementing the dependency property (the name of your MyTextColumn class)
In the MyTextColumn xaml, it should look something like this:
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox
FocusManager.FocusedElement="{Binding RelativeSource={RelativeSource Self}}"
Text="{Binding Path=IsSpinning, RelativeSource={RelativeSource AncestorType=DataGridTemplateColumn}, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"/>
I've put the original property name IsSpinning, you should put there your chosen name. Also, you might have to fix the relative source if the base class is not DataGridTemplateColumn. This should pick up anything comming to your custom control.
The final step is to use your control:
<controls:MyTextColumn IsSpinning="{binding PropName}"/>
You basically bind it to whatever string you want. Feel free to write up any problems that you might have with my explanation or code and I'll fix my answer accordingly.

How to set silverlight comboboxitem value in xaml

I create a datatemplate for a combobox as follows:
<DataTemplate x:Key="AircraftTypeTemplate">
<StackPanel Orientation="Horizontal" Width="340">
<ComboBox>
<ComboBoxItem>CJ1</ComboBoxItem>
<ComboBoxItem>CJ3</ComboBoxItem>
<ComboBoxItem>Bravo</ComboBoxItem>
<ComboBoxItem>Excel</ComboBoxItem>
<ComboBoxItem>Sovereign</ComboBoxItem>
</ComboBox>
</StackPanel>
</DataTemplate>
It renders fine, but I would like to be able to associate a value with each of the items without having to bind it to some data context. For example I would like the CJ1 comboboxitem to have a value of 5. How would I set those in XAML?
Like:
<ComboBoxItem Value="5">CJ1</ComboBoxItem>
Thanks!
You can set the Name property to be any arbitrary string and use that. For more flexibility, you can use the Tag property, which according to MSDN:
Gets or sets an arbitrary object value that can be used to store custom information about this object.
You can read more about Tag here. I'd say Tag is probably better as opposed to bending Name to your will, and you can stick a string into Tag just as easily as Name.

How do I dynamically change Header's DataGrid TextColumn?

I'm trying to build a DataGrid layout where the first column's name will be dinamically changed.
How can I do, into DataGridTextColumn's Header property, to change that? I've saw some examples than the Header property is connected into a StaticResource, but a StaticResource is a fixed value, and that's doesn't work for me,
once what I need is several values.
Example:
If a user select a RadioButton, filtering by hour, the header will be X
If filters by day, header will be Y
If Filters by month, header will be Z...
Remembering, this is one of several examples than i would need to change.
Thanks.
This can be done easily with Databinding.
The CodeBehind Way
Create a property in the codebehind of your window to hold the string value; I will call mine TextProp. I will assume the elementname of your window is "Window" for this example. In the DataGridTextColumn tag, databind the Header attribute to that property.
<DataGridTextColumn Header="{Binding TextProp, ElementName=Window}"/>
The MVVM Way
Do the same as above, except put the property on your viewmodel to which the datagrid is bound. Change the XAML to:
<DataGridTextColumn Header="{Binding TextProp}"/>
Then all you have to do is change that Property value in whatever way you choose. To get this to update the value when the property changes, you will need to implement INotifyPropertyChanged (Check at the bottom of that post).

Compare objects to set selected value in pre-loaded combobox silverlight MVVM

I'm trying to set seletected value to a pre-loaded combobox using silverlight with MVVM.
I load these combobox items before selecting value.
For example I have a combobox to select a country. My first step is to load a List which is bound to the combobox source. This is loading perfectly.
After this, I have a "SelectedCountry" object bound to the selectedItem of the combobox in a two-way binding.
This is working perfect when I select any of the combobox values and my SelectedCountry object is correctly selected.
The problem comes when I try to assign the selected value in my ViewModel. This way, the combobox selecteditem is not updated.
I suppose this is because, on fact, they are not the same object (they have the same values but they are diferent references).
Should this work if I re-implement the equals method? Or should I find the same object by searching into the List?? This would be very easy because this two countries would be the same if they have the same id... but I can have more complex objects and I think the equals method would be better.
Thanks in advance!!
Edit for adding some code example:
<ComboBox Grid.Column="7" Margin="6,0" Name="cBTipoPoliza" VerticalAlignment="Center" TabIndex="4" ItemsSource="{Binding TiposPolizas, Mode=OneWay}" SelectedItem="{Binding TipoPoliza, Mode=TwoWay}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding nombre_tipo}" />
</DataTemplate>
</ComboBox.ItemTemplate>
Usually I just override the .Equals() method to check if they are equal by ID or Name
You should try to avoid having multiple copies of the same object in memory at the same time. One method to do this is to have your VM be the source of objects. When you load the list have the VM do it and expose an AvailableCountries ObservableCollection property on the VM that your ComboBox can bind to.
If your objects are semantically equal based on ID, definitely override Equals and == and != and hashcode. However be careful because if you're enabling people to update the objects you can run into collisions (even within the same instance of the app) where one screen is holding onto stale data.

Resources