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.
Related
How can I bind Visibility of TooTip to ToolTipVisibility property which is in ViewModel?
I have MenuObject class,
public class MenuObject
{
public string Name { get; set; }
public string IconPath { get; set; }
}
MenuObjects collection, ToolTipVisibility property in ViewModel,
public class MainViewModel : Conductor<object>
{
private bool _toolTipVisibility;
private ObservableCollection<MenuObject> _menuItems;
public bool ToolTipVisibility
{
get { return _toolTipVisibility; }
set
{
_toolTipVisibility = value;
NotifyOfPropertyChange(() => ToolTipVisibility);
}
}
public ObservableCollection<MenuObject> MenuItems
{
get { return _menuItems; }
set
{
_menuItems = value;
NotifyOfPropertyChange(() => MenuItems);
}
}
public MainViewModel()
{
ToolTipVisibility = true;
}
public void ToggleVisibility()
{
ToolTipVisibility = !ToolTipVisibility;
}
}
and ListView binding with this collection
<ListView x:Name="MenuItems">
<ListView.ItemTemplate>
<DataTemplate>
<DataTemplate.Resources>
<BooleanToVisibilityConverter x:Key="b2vc"/>
</DataTemplate.Resources>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Path=IconPath}" Stretch="None" Margin="12,0,0,0"/>
<TextBlock Text="{Binding Path=Name}" Margin="25,0,0,0"/>
<StackPanel.ToolTip>
<ToolTip Content="{Binding Path=Name}"
Visibility="{Binding ..., Converter={StaticResource b2vc}}"/> <!--// How can i do this? //-->
</StackPanel.ToolTip>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Help me, please. Thank you! :)
UPDATE:
I tried many ways and still can't resovle it. But I found out something. If I put this
Visibility="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type Window}, AncestorLevel=1},
Path=DataContext.ToolTipVisibility, Converter={StaticResource b2vc}}"
in tag StackPanel, then it works fine. But if i put same thing in tag ToolTip, it doesn't work. What am I missing?
Good question. By visible I'm going to assume that you mean "visible when mouse over", since that is what Visibility does for the ToolTip property in WPF.
I used the following view model, which is quite similar to yours. I left out all other bindings than visibility, for simplicty:
private bool _isToolTipVisible;
// The 'ToolTip.Visibility' will be bound to this property
public bool IsToolTipVisible
{
get => _isToolTipVisible;
set
{
_isToolTipVisible = value;
NotifyOfPropertyChange(nameof(IsToolTipVisible));
}
}
// This is just so that I am able to demonstrate the effect
public void ChangeToolTipVisibility()
{
IsToolTipVisible = !IsToolTipVisible;
}
For the view, I did pretty much what you are already doing, just adding a binding to IsToolTipVisible, to control the visibilty of the tool tip. The button I added, is just to be able to demonstrate the effect (in calls the method ChangeToolTipVisibility():
<Window.Resources>
<BooleanToVisibilityConverter x:Key="b2vc"/>
</Window.Resources>
<Grid Margin="100">
<StackPanel Orientation="Horizontal">
<StackPanel.ToolTip>
<ToolTip Content="Lorem ipsum" Visibility="{Binding IsToolTipVisible, Converter={StaticResource b2vc}}"/>
</StackPanel.ToolTip>
<TextBlock Text="Button text" Margin="25,0,0,0"/>
<Button x:Name="ChangeToolTipVisibility" Content="Change visiblity" />
</StackPanel>
</Grid>
That's all it takes. So you were pretty much there already, assuming that I understood your question correctly :-)
Finally, I found a solution. Here is the asnwer https://stackoverflow.com/a/26223802/13230344
This is for my case:
<ToolTip Content="{Binding Path=Name}"
Visibility="{Binding DataContext.ToolTipVisibility,
Source={x:Reference MenuItems}, Converter={StaticResource b2vc}}/>
I'm new in WPF and try learn VMMV. I try to create TabControl with template for content in xaml.
I want tabitem with content of grid and in grid list of usercontrols. After adding a usercontrol the header of tabitem render right but nothing is in content. What is wrong?
This is my xaml:
<TabControl ItemsSource="{Binding Items}" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch" Visibility="Hidden" Name="tcContent" Grid.Column="1" Grid.Row="0">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Header}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<ContentControl Content="{Binding Content}" />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
Viewmodel:
public class Tab
{
public string Header { get; set; }
public ObservableCollection<UserControl> Content { get; set; }
}
public class MainWindowsViewModel
{
ObservableCollection<Tab> _items = new ObservableCollection<Tab>();
public ObservableCollection<Tab> Items
{
get
{
return _items;
}
}
}
Behind code for fill tabcontrol:
public MainWindow()
{
this.DataContext = new MainWindowsViewModel();
}
public void AddToTab(string header, UserControl c)
{
Tab tab = new Tab();
tab.Header = header;
tab.Content = new ObservableCollection<UserControl>();
tab.Content.Add(c);
((MainWindowsViewModel)this.DataContext).Items.Add(tab);
}
You are misunderstanding the MVVM principle.
In your view-model, you have a collection of the Tab objects, and each of them holds a collection of UserControls. In this way, your view-model contains some view elements (UserControls). In MVVM, you shouldn't do that.
Instead, you create the view-models for each tab item that describe a model of the representation (hence view-model); and in XAML, you describe how do these view-models should look like using DataTemplates.
But this is all required only if your views have to be dynamic. E.g. you don't know which data will be available because you're fetching them from a database.
If your TabItems display a set of UserControls that won't change, then just describe your view completely in XAML, without any DataTemplates.
Firstly, remove Visibility="Hidden" form the TabControl. Then change the ControlControl to ItemsControl which can hold Tab.Content, which is a collection. However, you should pay attention to the problems mentioned in #dymanoid's answer.
<TabControl.ContentTemplate>
<DataTemplate >
<ItemsControl ItemsSource="{Binding Content}" />
</DataTemplate>
</TabControl.ContentTemplate>
My XAML is as under. I have a main ViewModel which has a list of items and I want to display a property within this list
<ItemsControl ItemsSource="{Binding MyList}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Label Content="{Binding MyName, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"></Label>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The problem is that MyName is always blank although my list has two items.
The main VM class has this property below and I add items in the constructor
public ObservableCollection<InnerViewModel> MyList { get; set; }
My inner VM has
public class InnerViewModel
{
private string _MyName;
public string MyName
{
get
{
return _MyName;
}
set
{
_MyName = value;
OnPropertyChanged("MyName");
}
}
I do have OnPropertyChanged in place but I'm not pasting it here for simplicity. I think the problem is with the XAML but I'm not sure. How do I get the property MyName to be displayed in my list of items in the view?
Since you use MyList as the ItemsSource, the data source for the child elements will be MyList. So you do not need to use the RelativeSource.
In other words, this should work :
<ItemsControl ItemsSource="{Binding MyList}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Label Content="{Binding MyName}"></Label>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Try and remove the relative source part of the binding.
<DataTemplate>
<Label Content="{Binding MyName}"></Label>
</DataTemplate>
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.
I am trying to bind a list of string values to a listbox so that their values are listed line by line. Right now I use this:
<ListBox Margin="20" ItemsSource="{Binding Path=PersonNames}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Id}"></TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
But I don't know what I am supposed to put into the textblock, instead of Id, since they are all string values, not custom classes.
Also it complains not having to find the PersonNames when I have it inside MainPage, as MainPage.PersonNames.
I set the data context to:
DataContext="{Binding RelativeSource={RelativeSource Self}}"
I am doing it wrong?
If simply put that your ItemsSource is bound like this:
YourListBox.ItemsSource = new List<String> { "One", "Two", "Three" };
Your XAML should look like:
<ListBox Margin="20" Name="YourListBox">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Update:
This is a solution when using a DataContext. Following code is the viewmodel you will be passing to the DataContext of the page and the setting of the DataContext:
public class MyViewModel
{
public List<String> Items
{
get { return new List<String> { "One", "Two", "Three" }; }
}
}
//This can be done in the Loaded event of the page:
DataContext = new MyViewModel();
Your XAML now looks like this:
<ListBox Margin="20" ItemsSource="{Binding Items}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The advantage of this approach is that you can put a lot more properties or complex objects in the MyViewModel class and extract them in the XAML. For example to pass a List of Person objects:
public class ViewModel
{
public List<Person> Items
{
get
{
return new List<Person>
{
new Person { Name = "P1", Age = 1 },
new Person { Name = "P2", Age = 2 }
};
}
}
}
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
And the XAML:
<ListBox Margin="20" ItemsSource="{Binding Items}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Name}" />
<TextBlock Text="{Binding Path=Age}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
You should show us the code for PersonNames, and I am not sure I understand your question, but maybe you want to bind it like this:
<TextBlock Text="{Binding Path=.}"/>
or
<TextBlock Text="{Binding}"/>
This will bind to the current element in the list (assuming PersonNames is a list of strings). Otherwise, you will see the class name in the list.
If the items source is enumerable as string-entries, use the following:
<TextBlock Text="{Binding}"></TextBlock>
You can use this syntax on any object. Generally, the ToString() -method will then called to get the value. This is in many cases very handy. But beware that no change notification will occur.
You can do this without having to explicitly define the TextBlock control as a part of your ListBox (unless you want better formatting). The trick to getting the binding to trigger is using an ObservableCollection<string> instead of List<string>
Window1.xaml
<ListView Width="250" Height="50" ItemsSource="{Binding MyListViewBinding}"/>
Window1.xaml.cs
public Window1()
{
InitializeComponent();
DataContext = this;
// Need to initialize this, otherwise you get a null exception
MyListViewBinding = new ObservableCollection<string>();
}
public ObservableCollection<string> MyListViewBinding { get; set; }
// Add an item to the list
private void Button_Click_Add(object sender, RoutedEventArgs e)
{
// Custom control for entering a single string
SingleEntryDialog _Dlg = new SingleEntryDialog();
// OutputBox is a string property of the custom control
if ((bool)_Dlg.ShowDialog())
MyListViewBinding.Add(_Dlg.OutputBox.Trim());
}