DataTemplate with textbox for search - c#

I uses MasterDetailsView control. It has MasterHeaderTemplate property. I want to add a TextBox in order to implementation of items search. I don't understand how to do this. Because DataTemplate don't has a needed property. It's UWP app, don't WPF.
TextBlock got value by MasterHeader property. But how do other binding. For example, placeholder text, event handlers.
MasterHeader="{x:Bind ViewModel.Title}"
MasterHeaderTemplate="{StaticResource MasterHeaderTemplate}"
<DataTemplate
x:Key="MasterHeaderTemplate">
<StackPanel>
<TextBlock
Text="{Binding}"
Style="{StaticResource HeaderStyle}" />
<TextBox
PlaceholderText="{???}" />
</StackPanel>
</DataTemplate>

You can use the Binding.ElementName property to set the name of your MasterDetailsView to use as the binding source for the Binding. Then you can access its DataContext(e.g. your ViewModel) and bind the property from ViewModel with PlaceholderText. For example:
.xaml:
<Page.Resources>
<DataTemplate x:Key="MasterHeaderTemplate">
<StackPanel>
<TextBlock
Text="{Binding}" />
<TextBox PlaceholderText="{Binding ElementName=MyDetailView,Path=DataContext.PlaceholderText}"/>
</StackPanel>
</DataTemplate>
</Page.Resources>
<Grid>
<controls:MasterDetailsView
ItemsSource="{Binding Lists}"
x:Name="MyDetailView" MasterHeader="{Binding Title}" MasterHeaderTemplate="{StaticResource MasterHeaderTemplate}">
<controls:MasterDetailsView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"></TextBlock>
</DataTemplate>
</controls:MasterDetailsView.ItemTemplate>
</controls:MasterDetailsView>
</Grid>
.cs:
public MainPage()
{
this.InitializeComponent();
ViewModel = new MyViewModel();
ViewModel.Title = "Header";
ViewModel.PlaceholderText = "MyPlaceholderText";
this.DataContext = ViewModel;
}
private MyViewModel ViewModel { get; set; }

Related

TabControl not finding data templates for TabControl items

I am binding a collection of TabViewModel items to a TabControl. Each of these has a header string property, and a content property of my own custom type BaseTabContentViewModel, an abstract class which each actual tab data viewmodel implements. Eg ValuationTabViewModel which is a sub-class of BaseTabContentViewModel.
I add the new TabViewModel to the Observable<TabViewModel> for the TabControl to pick up and it shows in the UI. I have overridden style templates for the layout of the tab control and header which work fine. The only trouble is the content doesn't find the template in my resource dictionary based on its type, it just displays the full qualified class name of the viewmodel, showing that it is not finding a default template for this class.
Why isn't the ValuationTabViewModel that is being displayed, finding the datatemplate for this type below?
My main view model.
public ObservableCollection<TabViewModel> DetailTabs { get; }
var valuationTab = new TabViewModel(DetailTabConstants.ValuationTab, new ValuationTabViewModel(_eventAggregator, _errorNotifier, _windsorContainer));
DetailTabs = new ObservableCollection<TabViewModel> { valuationTab };
Main XAML
<TabControl Margin="0,-2,0,0" x:Name="SelectionTabs" Style="{StaticResource DetailTabControl}" ItemsSource="{Binding DetailTabs}"
SelectedValue="{Binding SelectedTab, Mode=TwoWay}" ItemContainerStyle="{StaticResource DetailTabItem}">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Header}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<TextBlock Text="{Binding Content}" />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
The content style template I want it to use
<DataTemplate x:Key="ValuationTabTemplate" DataType="{x:Type detailTabs1:ValuationTabViewModel}" >
<detailTabs:ValuationTab Margin="0,10,0,10" />
</DataTemplate>
And my Tab item ViewModel class
public class TabViewModel : ViewModelBase
{
private string _header;
private BaseTabContentViewModel _content;
public string Header
{
get => _header;
set
{
_header = value;
RaisePropertyChanged(nameof(Header));
}
}
public BaseTabContentViewModel Content
{
get => _content;
set
{
_content = value;
RaisePropertyChanged(nameof(Content));
}
}
public TabViewModel(string header, BaseTabContentViewModel viewModel)
{
Header = header;
Content = viewModel;
}
}
Remove the <TabControl.ContentTemplate> element and define an implicit DataTemplate (without an x:Key) for each type:
<TabControl Margin="0,-2,0,0" x:Name="SelectionTabs" Style="{StaticResource DetailTabControl}" ItemsSource="{Binding DetailTabs}"
SelectedValue="{Binding SelectedTab, Mode=TwoWay}" ItemContainerStyle="{StaticResource DetailTabItem}">
<TabControl.Resources>
<DataTemplate DataType="{x:Type detailTabs1:ValuationTabViewModel}">
<detailTabs:ValuationTab Margin="0,10,0,10" />
</DataTemplate>
</TabControl.Resources>
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Header}" />
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
I think your custom DataTemplate isn't being used because you have specified both a Key and a DataType for it, and the Key takes precedence.
As per the Microsoft docs:
... if you assign this DataTemplate an x:Key value, you are overriding the implicit x:Key and the DataTemplate would not be applied automatically
I would suggest removing the Key property and just using DataType:
<DataTemplate DataType="{x:Type detailTabs1:ValuationTabViewModel}">
...
</DataTemplate>
Also, as #mm8 implied, you are explicitly setting the ContentTemplate of your TabControl. You need to remove that from the XAML.

Force ListView to refresh or Invalidate in UWP

I have a ListView with ItemTemplate. I want to bind one control background in ItemTemplate to 2 properties, one of properties is in ItemsSource and onother one is in my page. since UWP has no multibinding support, I bind it to one property in ItemSource and for another property in my page I want to handle it in my code behind.
<ListView >
<ListView.ItemTemplate>
<DataTemplate>
<Border HorizontalAlignment="Stretch"
x:Name="myborder"
Padding="5,0,5,0"
Background="{Binding myProperty, Converter={StaticResource convertPropertyToBgColor },ConverterParameter=border}">
<StackPanel Padding="0,10,10,10"
Background="{Binding myProperty, Converter={StaticResource convertPropertyToBgColor},ConverterParameter=stack}">
<TextBlock Text="{Binding Text}">
</StackPanel>
</Border>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
in the convertPropertyToBgColor I get the brush from Resources.
and in code behind when my second desired property is changed I Change My resources. so the brush I have used from resources get changed and because of that I want to call that converter again to refresh Background, I called updateLayout but it doesn't refresh my ListView and it doesn't call myConvereter again. How can I force ListView to recreate or refresh Items that it has made?
Generally you class should implement INotifyPropertyChanged, then once you change the property, usually in its setter you also call OnPropertyChanged event which will update your UI. There are plenty examples of that, here is one.
The other way, may be to call Bindings.Update(), but normally you probably should use the method above.
To make my comments clearer - something like this is possible:
<StackPanel Orientation="Horizontal" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ListView x:Name="myList" Width="200">
<ListView.ItemTemplate>
<DataTemplate>
<Border HorizontalAlignment="Stretch"
x:Name="myborder"
Padding="5,0,5,0"
Background="{Binding Path=DataContext.MyProperty, ElementName=myList}">
<StackPanel Padding="0,10,10,10">
<TextBlock Text="{Binding}"/>
</StackPanel>
</Border>
</DataTemplate>
</ListView.ItemTemplate>
<x:String>Element 1</x:String>
<x:String>Element 2</x:String>
<x:String>Element 3</x:String>
</ListView>
<Button Content="Change" Click="Button_Click"/>
</StackPanel>
code behind:
public sealed partial class MainPage : Page, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
void RaiseProperty(string name) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
private SolidColorBrush myPropety = new SolidColorBrush(Colors.Red);
public SolidColorBrush MyProperty
{
get { return myPropety; }
set { myPropety = value; RaiseProperty(nameof(MyProperty)); }
}
public MainPage()
{
this.InitializeComponent();
this.DataContext = this;
}
private void Button_Click(object sender, RoutedEventArgs e) => MyProperty = new SolidColorBrush(Colors.Blue);
}

Binding to TextBlock in ListView

I don't know if my understanding of binding is just poor or if I am not seeing the problem, but I hope someone can help me out here. I have a ListView with a template of an image and a TextBlock, I need the TextBlock to be bound to the ItemsSource of the ListView. However when I run this I get nothing shown, I don't even see my image that I have set.
XAML:
<UserControl.Resources>
<FontFamily x:Key="FontFamily">MS Reference Sans Serif</FontFamily>
</UserControl.Resources>
<Grid>
<ListView BorderThickness="0" ItemsSource="{Binding Facies}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="../Images/Shale.png"/>
<TextBlock Text="{Binding FaciesName}" Width="75" Margin="5"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
C#:
public partial class FaciesControl : UserControl
{
public FaciesControl()
{
InitializeComponent();
}
public List<string> Facies {get; set;}
public void Bind(string[] data)
{
Facies = new List<string>();
Facies.AddRange(data);
}
}
First set DataContext like this:
public FaciesControl()
{
InitializeComponent();
string[] str = { "Name1", "Name2", "Name3" };
Bind(str); // Make sure you have called the Bind method
DataContext = Facies;
}
Second change your XAML like this:
<ListView BorderThickness="0" ItemsSource="{Binding}">
....
....
<TextBlock Text="{Binding}" Width="75" Margin="5"/>

How to correctly databind collection to TabControl?

I've looked at several qustions/answers on SO and for some reason I'm not getting anything to work for binding a collection to the TabControl. I am trying to do this so I don't have to assign the DataContext in the code-behind.
Here is the view model:
public class DocumentsCollectionViewModel : IEnumerable<DocumentViewModel> {
private readonly ObservableCollection<DocumentViewModel> mDocsCollection = new ObservableCollection<DocumentViewModel>();
public ObservableCollection<DocumentViewModel> Documents {
get { return mDocsCollection; }
}
// initially excluded from question as I thought it was understood :)
public IEnumerator<DocumentViewModel> GetEnumerator() {
return mDocsCollection.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() {
return mDocsCollection.GetEnumerator();
}
}
...for completeness sake, the DocumentViewModel:
public class DocumentViewModel {
private readonly Document mDocument;
public string Name {
get { return mDocument.Name; }
}
}
In the XAML, I am a little confused about where to tell the tab control to use the Documents property in DocumentsCollectionViewModel:
<TabControl Name="DocumentsTab"
ItemsSource="{Binding localmodels:DocumentsCollectionViewModel}">
<TabControl.ItemTemplate>
<DataTemplate DataType="{x:Type localmodels:DocumentViewModel}">
<Label Style="{StaticResource DefaultFont}"
Content="{Binding Name}"/>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate DataType="{x:Type localmodels:DocumentViewModel}">
<Label Style="{StaticResource DefaultFont}"
Content="{Binding Name}"/>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
Have you set the DataContext of your Window/UserControl which has this TabControl to the instance of your DocumentsCollectionViewModel?
Try doing this in the constructor of your Window containing the TabControl
public void MainWindow()
{
InitializeComponents();
this.DataContext = new DocumentsCollectionViewModel();
//Initialize the collection inside your VM
}
OR you can set DataContext in xaml like
<Window>
<Window.DataContext>
<localmodels:DocumentsCollectionViewModel/>
</Window.DataContext>
</Window>
then in your xaml just directly bind to Documents property
<TabControl Name="DocumentsTab"
ItemsSource="{Binding Documents}">
<TabControl.ItemTemplate>
<DataTemplate DataType="{x:Type localmodels:DocumentViewModel}">
<Label Style="{StaticResource DefaultFont}"
Content="{Binding Name}"/>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate DataType="{x:Type localmodels:DocumentViewModel}">
<Label Style="{StaticResource DefaultFont}"
Content="{Binding Name}"/>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>

ItemsControl inside my UserControl is not updating

I'm having trouble with my UserControl with following source code:
[ContentProperty("SetContent")]
public partial class HeaderContainer : UserControl
{
// Header region
public FrameworkElement SetContent
{
get { return (FrameworkElement)GetValue(SetContentProperty); }
set { SetValue(SetContentProperty, value); }
}
public static readonly DependencyProperty SetContentProperty =
DependencyProperty.Register("SetContent", typeof(FrameworkElement),
typeof(HeaderContainer), new PropertyMetadata(null));
public HeaderContainer()
{
InitializeComponent();
DataContext = this;
}
}
This is my XAML
<StackPanel>
<Border>
<TextBlock Text="{Binding Header}" />
</Border>
<ContentPresenter Content="{Binding SetContent}"/>
</StackPanel>
And here is my Problem:
<c:HeaderContainer Header="List">
<ItemsControl ItemsSource="{Binding ObjectList}" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding DisplayName}"/>
<TextBlock Text="{Binding SecondLine}" Foreground="Gray" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</c:HeaderContainer>
The Itemscontrol itself is working, also the UserControl, when I add content in XAML. The problem is, when I add Items to ObjectList while it's in the HeaderContainer - nothing happens. Where am I thinking wrong?
It is hard to determine the exact issue here because your code is incomplete. You have a SetContent dependency property, however your example usage doe not use it. I think your approach should be to sub-class ContentControl, adding your Header property to this.
Or ... just use the Silverlight Toolkit HeaderedContentControl which I think does exactly what you are trying to achieve!

Categories