I am trying to bind a List of items to a TabControl. The items look like:
class SciEditor
{
private Scintilla editor = null;
public System.Windows.Forms.Control Editor
{
get { return editor; }
}
private string path = null;
public string ShortName
{
get
{
return null == path ? "New Script" : Path.GetFileNameWithoutExtension(path);
}
}
....
In my main window, the List is called "allScripts". Here's the XAML:
<TabControl Grid.Row="0" Grid.Column="0" Name="tabControl1">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock>
<TextBlock Text="{Binding ShortName}"/>
</TextBlock>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<WindowsFormsHost Child="{Binding Editor}" />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
The problem is I can't set "Child" in WindowsFormsHost because
A 'Binding' cannot be set on the 'Child' property of type 'WindowsFormsHost'. A 'Binding' can only be set on a DependencyProperty of a DependencyObject.
How can I set the WindowsFormsHost child?
EDIT: forgot to mention, in the main window constructor I have:
tabControl1.ItemsSource = allScripts;
Change your content template to
<TabControl.ContentTemplate>
<DataTemplate>
<ContentControl Content="{Binding Editor}" />
</DataTemplate>
</TabControl.ContentTemplate>
and change the Editor property of your code-behind to
public WindowsFormsHost Editor
{
get { return new WindowsFormsHost(){Child=editor}; }
}
Related
I have a list view control that uses the DataTemplate to bind the the data.
After I created the dependency the binding works with a simple string but will not work when I bind the class property. If I bind the data to a textBlock it works, but if I bind the same thing to my User Control it doesn't work.
Here is my XAML: LISTVIEW
<ListView x:Name='lbUsers'
Height='370'
Canvas.Left='264'
Canvas.Top='183'
Width='1177'
Background='{x:Null}'>
<ListView.ItemTemplate>
<DataTemplate>
<WrapPanel>
<Views:UserSelectRibbon NameText ="{Binding Name}" />
<Image Width='10' />
</WrapPanel>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Width="{Binding (FrameworkElement.ActualWidth),
RelativeSource={RelativeSource AncestorType=ScrollContentPresenter}}"
ItemWidth="{Binding (ListView.View).ItemWidth,
RelativeSource={RelativeSource AncestorType=ListView}}"
MinWidth="{Binding ItemWidth, RelativeSource={RelativeSource Self}}"
ItemHeight="{Binding (ListView.View).ItemHeight,
RelativeSource={RelativeSource AncestorType=ListView}}" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
HERE IS MY USER CONTROL:
public string NameText
{
get { return (string)GetValue(NameTextProperty); }
set { SetValue(NameTextProperty, value); }
}
public static readonly DependencyProperty NameTextProperty =
DependencyProperty.Register("NameText", typeof(string), typeof(UserSelectRibbon),null);
public UserSelectRibbon()
{
InitializeComponent();
DataContext = this;
}
HERE IS MY SIMPLE USER CLASS :
public class User {
public string Name { get; set; }
public int Age { get; set; }
public int Playlist { get; set; }
}
SO:
In the XAML if I do this : WORKS
<Views:UserSelectRibbon NameText ="Some text here" />
This will work and add the text to the TextBlock in the user control
BUT:
In the XAML if I do this : DOESN'T"T WORK
<Views:UserSelectRibbon NameText ="{Binding Name}" />
I would like to know why it works without the binding but it doesn't work with the binding
The problem is with your DataContext:
public UserSelectRibbon()
{
InitializeComponent();
DataContext = this;
}
This has an implication on the following:
<Views:UserSelectRibbon NameText ="{Binding Name}" />
In here, the DataContext is already changed to the Views:UserSelectRibbon, so you can't bind to anything from the outer DataContext anymore.
Solution: do not set the DataContext of a UserControl to itself from the inside. Never!
Instead, set it on an element inside the usercontrols tree or use some RelativeSource or ElementName binding.
Solution with inner DataContext:
<UserControl ...>
<Grid x:Name="grid1">
<TextBlock Text="{Binding NameText}"/>
</Grid>
</UserControl>
and
public UserSelectRibbon()
{
InitializeComponent();
grid1.DataContext = this; // set DataContext on inner control instead of the usercontrol itself
}
Solution with RelativeSource (you can use UserControl base type or your specific usercontrols inner type):
<TextBlock Text="{Binding NameText,RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"/>
Solution with ElementName:
<UserControl ...
x:Name="myControl">
<Grid>
<TextBlock Text="{Binding NameText,ElementName=myControl}"/>
</Grid>
</UserControl>
I did something like this recently with an itemsControl. I'm probably gonna forget everything though.
Anyway, there's a simpler way then binding straight from the item itself. Instead, you call the item from within your c# and have the binding there. After
Mainpage()
{
InitializeComponent();
}
Still in the brackets, you should add this:
List<SampleItem> = new List<SampleItem>;
items.Add({NameText = Name});
lbusers.ItemsSource = items;
And outside the brackets:
public class SampleItem{
public string Name {get; set;}
}
You can add the items.Add line as many times as you want, and that's how many items will show up.
In the above image:
You may note there is a little rectangle box around the tabHeader "title".
When I click inside the rectangle box, the tab does not get selected.
When I click outside the box, it does.
Code C#:
public class Lexicon : ObservableCollection<LexiconEntry>
{
public String leftLanguage { get; set; }
public String rightLanguage { get; set; }
public String name { get; set; }
public Lexicon(String name, String leftLanguage,String rightLanguage)
{
this.leftLanguage = leftLanguage;
this.rightLanguage = rightLanguage;
this.name = name;
}
}
public partial class MainWindow : System.Windows.Window
{
public List<Lexicon> lexicons;
public MainWindow()
{
InitializeComponent();
lexicons = new List<Lexicon>();
lexicons.Add(new Lexicon("foo_title","russian","french"));
lexicons.Add(new Lexicon("bar_title", "french", "english"));
lexicons.Add(new Lexicon("baz_title", "russian", "french"));
TheTabControl.ItemsSource = lexicons;
}
}
Xaml CODE:
<Window x:Class="InterpreterNotepad.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:toolkit="http://schemas.xceed.com/wpf/xaml/toolkit"
xmlns:avalonDock="http://schemas.xceed.com/wpf/xaml/avalondock"
Title="InterpreterNotepad" WindowStartupLocation="CenterScreen" WindowState="Maximized" x:Name="mainWindow">
...
<DockPanel>
<Menu DockPanel.Dock="Top">
...
</Menu>
<TabControl x:Name="TheTabControl">
<TabControl.ItemTemplate>
<DataTemplate>
<TabItem Header="{Binding name}"/>
</DataTemplate>
</TabControl.ItemTemplate>
<!-- Content -->
<TabControl.ContentTemplate>
<DataTemplate>
<TextBlock Text="bqr"/>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</DockPanel>
</Window>
Got it, Sorry for this newbie question, I'm new to WPF. Using snoop, I saw that my rectangle box was actually a tabItem inside a tabItem!
I thought the dataTemplate described the template to be repeated in the tabControl, while it describes the content of each tabItem.
I Need to put:
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</TabControl.ItemTemplate>
Instead of:
<TabControl.ItemTemplate>
<DataTemplate>
<TabItem Header="{Binding name}"/>
</DataTemplate>
</TabControl.ItemTemplate>
My class looks like this:
public class testclass
{
public List<otherClass> references { get { return _references; } }
}
My otherClass looks like this
public class otherClass
{
public string name { get; set; }
}
And now i try to access this "otherClass" inside a DataTemplate
<DataTemplate x:Key="templateCore" DataType="{x:Type vm:AdminInterfaceViewModel}" >
<GroupBox DataContext="{Binding references }">
...
</DataTemplate>
this works fine, or i think at least, beaucse intellisense autocomplete it. But now how can i get access to the name property of the "otherClass" ?
All you need is to binding the List to a ItemsControl type,such as ListBox,DataGrid etc,and the ItemsControl will use the 'otherClass' instance in the List as the DataContext for each item in it.So you can find a 'mapping' there:
'List<otherClass>'--'ItemsControl'
'otherClass'--'Item'
.
I suppose that 'AdminInterfaceViewModel' is your DataContext,and 'references' is one property of it, so try this:
<DataTemplate x:Key="templateCore" DataType="{x:Type vm:AdminInterfaceViewModel}" >
<GroupBox>
<ListBox ItemsSource="{Binding references}">
<ListBox.ItemTemplate>
<DataTemplate>
<TexBox Text="{Binding name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</GroupBox>
</DataTemplate>
> Update:
1.Suppose that you have a MainViewModel which contains a property named MyViewModel in type of 'AdminInterfaceViewModel '.
class MainViewModel
{
public AdminInterfaceViewModel MyViewModel {get; set;}
}
2.You have set the 'MainViewModel' as the DataContext of your Window,then you can use the property 'MyViewModel' in xaml.
<Window>
<Grid>
<ContentControl Margin="20" Content="{Binding MyViewModel}">
</ContentControl>
</Grid>
</Window>
3.Define the DataTemplate in your ResourceDictionary such as 'generic.xaml'.Remove the x:Key then the DataTemplate will automatically applied to every 'AdminInterfaceViewModel' type instance.
<DataTemplate x:Key="templateCore" DataType="{x:Type vm:AdminInterfaceViewModel}" >
<GroupBox>
<ListBox ItemsSource="{Binding references}">
<ListBox.ItemTemplate>
<DataTemplate>
<TexBox Text="{Binding name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</GroupBox>
</DataTemplate>
> Tips:
Check this link,it may solve your potential problems:MVVM pattern
I am trying to create a user control that contains a list box and I can't figure out how to properly setup the databinding.
In the MainForm.xaml (MyItems is a ObservableCollection defined in the ViewModel):
<my:ItemsList Items="{Binding MyItems}"/>
The user contol:
public partial class ItemsList : UserControl
{
public ItemsList()
{
InitializeComponent();
}
public IEnumerable Items
{
get { return (IEnumerable)GetValue(ItemsProperty); }
set { SetValue(ItemsProperty, value); }
}
public static readonly DependencyProperty ItemsProperty =
DependencyProperty.Register("Items", typeof(IEnumerable), typeof(ItemsList), null);
}
And the xaml (namespaces declarations omitted):
<UserControl x:Class="MyApp.Controls.ItemsList">
<phone:LongListSelector ItemsSource="{Binding Items}">
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ItemName}" />
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
</UserControl>
The error I'm getting: BindingExpression path error: 'Items' property not found on 'MyApp.ViewModels.MainViewModel' ?!?
What I was missing was setting the data context for the listbox in the constructor of the user control...
LayoutRoot.DataContext = this;
Check: do you use correct DataContext of your page(must be your ViewModel)?
The user contol must be:
public partial class ItemsList : UserControl
{
public ItemsList()
{
InitializeComponent();
}
public IEnumerable Items
{
get { return (IEnumerable)GetValue(ItemsProperty); }
set { SetValue(ItemsProperty, value); }
}
public static readonly DependencyProperty ItemsProperty = DependencyProperty.Register("Items", typeof(IEnumerable), typeof(ItemsList), new PropertyMetadata(ItemsChanged));
private static void ItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var controll = (ItemsList)d;
var val = (IEnumerable)e.NewValue;
controll.lls.ItemSource = val;
}
Xaml
<UserControl x:Class="MyApp.Controls.ItemsList">
<phone:LongListSelector x:name="lls">
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ItemName}" />
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
</UserControl>
hope its help
In your xaml you need to add a reference to the viewmodel and make it the datacontext for the control.
<UserControl xmlns:local="clr-namespace:"myproject.mynamespace;assembly=myproject">
<UserControl.Resources>
<local:myviewmodel x:key="viewModel"/>
</UserControl.Resources>
<UserControl.DataContext>
<Binding Source="{StaticResource viewModel}"/>
</UserControl.DataContext>
<phone:LongListSelector ItemsSource="{Binding Items}">
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ItemName}" />
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
</UserControl>
Just a note: You can use the DisplayMemberPath="ItemName" attribute instead of a data template unless you need to interact with the textblock in some way.
Hope this helps.
I have a ListBox bound to a source which provides data to the text property of the controls inside. Now I'd like to bind Foreground property of my textboxes to a different source other than the one the main ListBox is bound to!
My listbox is bound to a ObservableCollection and I want my textblock Foreground property bound to textColor which is located in ViewModel
public SolidColorBrush textColor
{
get { return new SolidColorBrush(Colors.Red); }
}
both are in ViewModel class.
I tried using Foreground="{Binding textColor}" but it seems XAML doesn't see it at all, should I do anything in the page so that it can see it, or is it because the parent (ListBox) is using a different source?
Edit :
More details:
I have a DataContext.cs class which I have defined my tables in it.
I have a ViewModel.cs class which I have these in it
public class CViewModel : INotifyPropertyChanged
{
private CDataContext myDB;
public CViewModel(string DBConnectionString)
{
myDB = new CDataContext(DBConnectionString);
}
private ObservableCollection<Groups> _allGroups;
public ObservableCollection<Groups> AllGroups
{
get { return _allGroups; }
set
{
_allGroups = value;
NotifyPropertyChanged("AllGroups");
}
}
public string textColor
{
get { return "Tomato"; }
}
}
Then I have my XAML file MainPage.xaml:
....
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<ListBox Margin="0,8,0,0" toolkit:TiltEffect.IsTiltEnabled="True" x:Name="list" ItemsSource="{Binding AllGroups}" HorizontalAlignment="Center" BorderThickness="4">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Background="Orange" Width="125" Height="125" Margin="6" Tap="Counterlist_OnTap">
<TextBlock Name="gname" Foreground="White" Text="{Binding Name}" VerticalAlignment="Center" HorizontalAlignment="Center" TextAlignment="Center" TextWrapping="Wrap"/>
<TextBlock Name="ccl" Margin="0,0,0,-5" Foreground="{Binding textColor}" Text="{Binding Count}" FontSize="26" VerticalAlignment="Bottom" HorizontalAlignment="Left" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
....
I also set DataContext of my MainPage to ViewModel in code behind:
this.DataContext = App.ViewModel;
The textColor property is part of the CViewModel, not the Groups object that is the datacontext within the ItemTemplate.
Within the ItemTemplate you can reach out to the parent ViewModel with the following Element binding:
<TextBlock Name="ccl" Margin="0,0,0,-5"
Foreground="{Binding ElementName=ContentPanel, Path=DataContext.textColor}"
Text="{Binding Count}" FontSize="26"
VerticalAlignment="Bottom" HorizontalAlignment="Left" />
All you have to do is declare a static class (e.g. a singleton with per-instance access) and explicitly set the property binding to look in that class instead of the parent-bound model.
Bottom line: Just set the Source explicitly through a StaticResource.