89

I am trying to build a dropdown list for a winform interop, and I am creating the dropdown in code. However, I have a problem getting the data to bind based on the DataTemplate I specify.

What am I missing?

drpCreditCardNumberWpf = new ComboBox();  
DataTemplate cardLayout = new DataTemplate {DataType = typeof (CreditCardPayment)};   
StackPanel sp = new StackPanel
{
    Orientation = System.Windows.Controls.Orientation.Vertical
};   

TextBlock cardHolder = new TextBlock {ToolTip = "Card Holder Name"};
cardHolder.SetBinding(TextBlock.TextProperty, "BillToName");
sp.Children.Add(cardHolder);

TextBlock cardNumber = new TextBlock {ToolTip = "Credit Card Number"};
cardNumber.SetBinding(TextBlock.TextProperty, "SafeNumber");
sp.Children.Add(cardNumber);

TextBlock notes = new TextBlock {ToolTip = "Notes"};
notes.SetBinding(TextBlock.TextProperty, "Notes");
sp.Children.Add(notes);

cardLayout.Resources.Add(sp, null);

drpCreditCardNumberWpf.ItemTemplate = cardLayout;
  • 4
    Please note that while these answers were correct at the time, the current recommended way to programmatically create a template is to load XAML from a string or a memory stream using the `Load` method of the `XamlReader` class. – Sheridan Mar 22 '18 at 15:06

3 Answers3

160

Assuming that you've already set up the ItemsSource etc for drpCreditCardNumberWpf...

//create the data template
DataTemplate cardLayout = new DataTemplate();
cardLayout.DataType = typeof(CreditCardPayment);

//set up the stack panel
FrameworkElementFactory spFactory = new FrameworkElementFactory(typeof(StackPanel));
spFactory.Name = "myComboFactory";
spFactory.SetValue(StackPanel.OrientationProperty, Orientation.Horizontal);

//set up the card holder textblock
FrameworkElementFactory cardHolder = new FrameworkElementFactory(typeof(TextBlock));
cardHolder.SetBinding(TextBlock.TextProperty, new Binding("BillToName"));
cardHolder.SetValue(TextBlock.ToolTipProperty, "Card Holder Name");
spFactory.AppendChild(cardHolder);

//set up the card number textblock
FrameworkElementFactory cardNumber = new FrameworkElementFactory(typeof(TextBlock));
cardNumber.SetBinding(TextBlock.TextProperty, new Binding("SafeNumber"));
cardNumber.SetValue(TextBlock.ToolTipProperty, "Credit Card Number");
spFactory.AppendChild(cardNumber);

//set up the notes textblock
FrameworkElementFactory notes = new FrameworkElementFactory(typeof(TextBlock));
notes.SetBinding(TextBlock.TextProperty, new Binding("Notes"));
notes.SetValue(TextBlock.ToolTipProperty, "Notes");
spFactory.AppendChild(notes);

//set the visual tree of the data template
cardLayout.VisualTree = spFactory;

//set the item template to be our shiny new data template
drpCreditCardNumberWpf.ItemTemplate = cardLayout;

You can use the same way I have set the ToolTip on the TextBlocks to set other properties such as margins.

Léo Léopold Hertz 준영
  • 134,464
  • 179
  • 445
  • 697
Donnelle
  • 5,689
  • 3
  • 27
  • 31
  • 2
    in silverlight 4 frameworkelementfactory class is not there. I dont want to use xaml.load also.. is there any other way with which we can solve it? – curiosity Jul 18 '11 at 10:45
  • 2
    For silverlight 4/5... ref.: http://blogs.msdn.com/b/scmorris/archive/2008/04/14/defining-silverlight-datagrid-columns-at-runtime.aspx – Nordes May 02 '12 at 09:44
  • 3
    See also: http://www.codeproject.com/Articles/444371/Creating-WPF-Data-Templates-in-Code-The-Right-Way – Daniel Hilgarth May 21 '13 at 16:04
6

The full version

var ms = new MemoryStream(Encoding.UTF8.GetBytes(@"<DataTemplate xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
                                                                 xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml""                                                                             
                                                                 xmlns:c=""clr-namespace:MyApp.Converters;assembly=MyApp"">
        <DataTemplate.Resources>
            <c:MyConverter x:Key=""MyConverter""/>
        </DataTemplate.Resources>
        <TextBlock Text=""{Binding ., Converter={StaticResource MyConverter}}""/>
      </DataTemplate>"));
var template = (DataTemplate)XamlReader.Load(ms);

var cb = new ComboBox { };
//Set the data template
cb.ItemTemplate = template;
Pavlo Datsiuk
  • 1,024
  • 9
  • 17
  • 1
    Note - `XamlReader.Load` doesn't accept event handlers. – Mikhail Orlov Jun 16 '20 at 14:40
  • This is the solution I need to use, because if you use the programmatic (setting visual tree etc) it will not be able to correctly resolve all element name bindings – toeb Jan 17 '23 at 08:09
0

Well, indeed we still have another way, you will really like it if you dislike those FrameworkElementFactory things.

And I think it just makes minor changes to the natural code, that is, declare a UserControl and put your control into it, and then, use just one FrameworkElementFactory to call the UserControl.

Simple demo code (in F#):

let buildView()=StackPanel()
//Build it with natural code
type MyView()=inherit UserControl(Content=buildView())
let factory=FrameworkElementFactory(typeof<MyView>)
let template=DataTemplate(VisualTree=factory)
let list=ItemsControl(ItemsSource=makeData(),ItemTemplate=template)
CharithJ
  • 46,289
  • 20
  • 116
  • 131
a_a
  • 271
  • 1
  • 8