Why doesn't TextBlock bind? - c#

When I run this simple wpf app, I get a blank window. Any ideas what I'm doing wrong?
//MainWindow.xaml.cs
public string SimpleText {get;set;}
public MainWindow()
{
InitializeComponent();
SimpleText = "this is a test";
}
//MainWindow.xaml
<StackPanel>
<TextBlock Text="{Binding SimpleText}" Width="200"/>
</StackPanel>

DataContext is a way to go but you can also use RelativeSource markup extension to get window's property:
<TextBlock Text="{Binding SimpleText, RelativeSource={RelativeSource
Mode=FindAncestor, AncestorType=Window}}" Width="200"/>

You must set the DataContext:
public MainWindow()
{
InitializeComponent();
SimpleText = "this is a test";
this.DataContext = this;
}
As an alternative, you can set DataContext on the side XAML like this:
XAML
<Window x:Class="TextBlockDontBind.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:this="clr-namespace:TextBlockDontBind"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<this:TestData />
</Window.DataContext>
<StackPanel>
<TextBlock Text="{Binding SimpleText}" Width="200"/>
</StackPanel>
</Window>
Code-behind
public class TestData
{
private string _simpleText = "this is a test";
public string SimpleText
{
get
{
return _simpleText;
}
set
{
_simpleText = value;
}
}
}
But in this case to update a property, for a Class must implement the INotifyPropertyChanged interface.

Related

Unable to pass property name dynamically to ListView.ItemTemplate to bind (UWP)

I have a user control like this
<UserControl
x:Class="App41.UserCntrl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d">
<Grid>
<ListView SelectionMode="Multiple" x:Name="lview" ItemsSource="{x:Bind ItemsSource, Mode=OneWay}" >
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{x:Bind PropertyNameToBind, Mode=OneWay}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
code behind
public sealed partial class UserCntrl : UserControl
{
public UserCntrl()
{
this.InitializeComponent();
}
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(UserControl),
new PropertyMetadata(null));
public IEnumerable ItemsSource
{
get => (IEnumerable)GetValue(ItemsSourceProperty);
set => SetValue(ItemsSourceProperty, value);
}
public string PropertyNameToBind
{
get { return (string)GetValue(PropertyNameToBindProperty); }
set { SetValue(PropertyNameToBindProperty, value); }
}
public static readonly DependencyProperty PropertyNameToBindProperty =
DependencyProperty.Register("PropertyNameToBind", typeof(string), typeof(UserControl), new PropertyMetadata(""));
}
And I am calling this user control in my main page like this
<Page
x:Class="App41.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App41" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d">
<Grid>
<local:UserCntrl x:Name="lview" Loaded="EditTextControl_Loaded"></local:UserCntrl>
</Grid>
Code behind
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
private void EditTextControl_Loaded(object sender, RoutedEventArgs e)
{
ObservableCollection<OptionItem> io = new ObservableCollection<OptionItem>();
io.Add(new OptionItem { Name = "11111111111" });
lview.ItemsSource = io;
lview.PropertyNameToBind = "Name";
}
}
public class OptionItem
{
private string _Name = string.Empty;
public string Name
{
get { return _Name; }
set { _Name = value; }
}
}
All looks good to me, but ListView displays empty items instead of my content. I believe the issue is in this line
<TextBlock Text="{x:Bind PropertyNameToBind, Mode=OneWay}" />
Where I am trying to bing Name property inside the OptionItem Model. How can I solve this?
In UWP, each DataTemplate corresponds to an item in the ItemsSource, so the DataContext of the DataTemplate is limited to the item itself, specific to your project, is OptionItem.
You use {x:Bind PropertyNameToBind} is actually trying to bind OptionItem.PropertyNameToBind, but your OptionItem does not have this property, so nothing will be displayed.
Please determine whether you want to bind PropertyNameToBind or OptionItem.Name property, if it is OptionItem.Name, please use {x:Bind Name}.
If not, try this:
<UserControl
...
x:Name="Main">
<Grid>
<ListView SelectionMode="Multiple" x:Name="lview" ItemsSource="{x:Bind ItemsSource, Mode=OneWay}" >
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ElementName=Main,Path=PropertyNameToBind}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</UserControl>
Update
I think I understand what you mean, but you have some misunderstandings about binding.
What you bind is a property, and you pass a string value. This value cannot be used as a property name to reflect the properties of the binding class.
Combined with your needs, I recommend that you use the interface for dynamic processing:
interface
public interface ITest
{
string GetDisplayText();
}
class
public class OptionItem : ITest
{
public string Name { get; set; } = string.Empty;
public string GetDisplayText()
{
return Name;
}
}
control
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:ITest">
<TextBlock Text="{x:Bind GetDisplayText()}" />
</DataTemplate>
</ListView.ItemTemplate>
By creating an interface, a method for outputting displayed text is provided.
Any class that inherits this interface can override this method according to their needs.
Inside the control, you only need to call the GetDisplayText() method, regardless of which class is bound.
<Grid>
<ListView SelectionMode="Multiple" x:Name="lview" ItemsSource="{x:Bind ItemSource, Mode=OneWay}" >
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{x:Bind OptionItem.Name, Mode=OneWay}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</Grid>

Wpf Databinding with ViewModel as property

to keep my MainWindow.xaml.cs clean I tried to outsource everything to a ViewModel class and then bind my view to properties of my ViewModel property
However using this extra layer doesn't seem to work. My List- and Textbox just stay empty. Any way to get this method working?
MainWindow class:
public partial class MainWindow : Window, INotifyPropertyChanged
{
public ViewModel ViewModel { get; set; }
public MainWindow()
{
InitializeComponent();
ViewModel = new ViewModel();
}
}
ViewModel class:
public class ViewModel : INotifyPropertyChanged
{
private string m_oneLineOnly;
private ObservableCollection<string> m_sampleLines;
public ObservableCollection<string> SampleLines
{
get => m_sampleLines;
set
{
m_sampleLines = value;
RaisePropertyChanged(nameof(SampleLines));
}
}
public string OneLineOnly
{
get => m_oneLineOnly;
set
{
m_oneLineOnly = value;
RaisePropertyChanged(nameof(OneLineOnly));
}
}
public ViewModel()
{
SampleLines = new ObservableCollection<string>(new List<string>()
{
"Gedanken",
"Zeit",
"Sand"
});
OneLineOnly = "Hello World";
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
private void RaisePropertyChanged(string propertyName)
{
var handlers = PropertyChanged;
handlers(this, new PropertyChangedEventArgs(propertyName));
}
}
MainWindow XAML
<Window x:Class="DataContext_Test_Project.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:DataContext_Test_Project"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800"
x:Name="Window">
<Grid>
<ListBox
HorizontalAlignment="Left"
Height="100"
Margin="334,132,0,0"
VerticalAlignment="Top"
Width="100"
ItemsSource="{Binding ElementName=Window, Path=ViewModel.SampleLines}"/>
<TextBox
HorizontalAlignment="Left"
Height="23"
Margin="184,166,0,0"
TextWrapping="Wrap"
VerticalAlignment="Top"
Width="120"
DataContext="{Binding ElementName=Window, Path=ViewModel}"
Text="{Binding Path=OneLineOnly}"/>
</Grid>
You can also define the DataContext in code behind:
public partial class MainWindow : Window
{
public ViewModel ViewModel { get; set; }
public MainWindow()
{
InitializeComponent();
ViewModel = new ViewModel();
DataContext = ViewModel;
}
}
And clean your XAML:
<Grid>
<ListBox
HorizontalAlignment="Left"
Height="100"
Margin="334,132,0,0"
VerticalAlignment="Top"
Width="100"
ItemsSource="{Binding SampleLines}"/>
<TextBox
HorizontalAlignment="Left"
Height="23"
Margin="184,166,0,0"
TextWrapping="Wrap"
VerticalAlignment="Top"
Width="120"
Text="{Binding OneLineOnly}"/>
</Grid>
In order to make Bindings like
ItemsSource="{Binding ElementName=Window, Path=ViewModel.SampleLines}"
DataContext="{Binding ElementName=Window, Path=ViewModel}"
work, you have two options. Either make the ViewModel property fire a property change notification, or simply initialize it before the XAML is parsed, i.e. before InitializeComponent() is called:
Like this:
public partial class MainWindow : Window
{
public ViewModel ViewModel { get; }
public MainWindow()
{
ViewModel = new ViewModel();
InitializeComponent();
}
}
Or simpler:
public partial class MainWindow : Window
{
public ViewModel ViewModel { get; } = new ViewModel();
public MainWindow()
{
InitializeComponent();
}
}
Remove the following line from xaml.cs file : ViewModel = new ViewModel();
Use the following code for Xaml file:
<Window x:Class="DataContext_Test_Project.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:DataContext_Test_Project"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800"
x:Name="Window" DataContext="{DynamicResource ViewModel}">
<Window.Resources>
<local:ViewModel x:Key="ViewModel"/>
</Window.Resources>
<Grid>
<ListBox
HorizontalAlignment="Left"
Height="100"
Margin="334,132,0,0"
VerticalAlignment="Top"
Width="100"
ItemsSource="{Binding Path=SampleLines}"/>
<TextBox
HorizontalAlignment="Left"
Height="23"
Margin="184,166,0,0"
TextWrapping="Wrap"
VerticalAlignment="Top"
Width="120"
Text="{Binding Path=OneLineOnly}"/>
</Grid>
</Window>

Expose a UserControls ContentTemplate

I'm trying to make my fist UserControl in C#. It is a TabControll with some quality of life improvements. The goal is to be able to use it in various projects, so it has to be as generic as possible.
So far I have exposed the ItemSource through a DependencyProperty. But I'm suck with how to do the same with the ContentTemplate Property.
Here's an example of my code so far:
XAML:
<UserControl ...>
<UserControl.Resources>
<!-- some styles and templates -->
</UserControl.Resources>
<TabControl ItemsSource="{Binding ItemsSource}" SelectedIndex="{Binding selectedIndex}"
Style="{StaticResource FixatedTabControl}" ItemTemplateSelector="{StaticResource myDataTemplateSelector}"/>
</UserControl>
The code behind:
public partial class DynamicTabControl : UserControl, INotifyPropertyChanged
{
public DynamicTabControl()
{
InitializeComponent();
this.DataContext = this;
}
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register("ItemsSource", typeof(IEnumerable<ITabItem>), typeof(DynamicTabControl));
public IEnumerable<ITabItem> ItemsSource
{
get { return (IEnumerable<ITabItem>)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
}
I can use the DynamicTabControl like so:
<Window x:Class="Demo.MainWindow"
...
xmlns:local="clr-namespace:Demo"
xmlns:foo="clr-namespace:DynamicTabUserControl"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<foo:DynamicTabControl x:Name="testTabContr" ItemsSource="{Binding data}">
</foo:DynamicTabControl>
</Grid>
</Window>
But how can I make it possible to alter/add the contenTemplate of the UserControl's TabControl?
I would like to get it to behave like such:
<Window x:Class="Demo.MainWindow"
...
xmlns:local="clr-namespace:Demo"
xmlns:foo="clr-namespace:DynamicTabUserControl"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<foo:DynamicTabControl x:Name="testTabContr" ItemsSource="{Binding data}">
<foo:DynamicTabControl.TabControl.ContentTemplate>
<DataTemplate>
<TextBox Text="{Binding someData}"/>
</DataTemplate>
</foo:DynamicTabControl.TabControl.ContentTemplate>
</foo:DynamicTabControl>
</Grid>
</Window>
I'm still learning, so please help me out.
Thank you in advance.
Add another dependency property to the UserControl:
public partial class DynamicTabControl : UserControl, INotifyPropertyChanged
{
public DynamicTabControl()
{
InitializeComponent();
}
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register("ItemsSource", typeof(IEnumerable<ITabItem>), typeof(DynamicTabControl));
public IEnumerable<ITabItem> ItemsSource
{
get { return (IEnumerable<ITabItem>)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public static readonly DependencyProperty TabContentTemplateProperty =
DependencyProperty.Register("TabContentTemplate", typeof(DataTemplate), typeof(DynamicTabControl));
public DataTemplate TabContentTemplate
{
get { return (DataTemplate)GetValue(TabContentTemplateProperty); }
set { SetValue(TabContentTemplateProperty, value); }
}
}
...and bind to it:
<UserControl>
<UserControl.Resources>
<!-- some styles and templates -->
</UserControl.Resources>
<TabControl ItemsSource="{Binding ItemsSource, RelativeSource={RelativeSource AncestorType=UserControl}}"
Style="{StaticResource FixatedTabControl}"
ContentTemplate="{Binding TabContentTemplate, RelativeSource={RelativeSource AncestorType=UserControl}}"/>
</UserControl>
You can then set this property in the XAML markup of the window:
<foo:DynamicTabControl x:Name="testTabContr" ItemsSource="{Binding data}">
<foo:DynamicTabControl.TabContentTemplate>
<DataTemplate>
<TextBox Text="{Binding someData}"/>
</DataTemplate>
</foo:DynamicTabControl.TabContentTemplate>
</foo:DynamicTabControl>

Defining UserControl Properties and binding them in windows phone

In my windows phone application, I made a user control that has some text blocks and images. It's something like a control to show posts like Facebook. I want to use this user control in a long list selector, but whenever I try to bind data to it I get no data. Please help me.
This is the MainPage.xaml
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<phone:LongListSelector Name="myLLS">
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<local:Posts TitleText={Binding TitleText}>
</local:Posts>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
</Grid>
And this is the code behind
public class TestData
{
private string _TitleText;
public string TitleText
{
get { return _TitleText; }
set { _TitleText = value; }
}
public TestData(string Text)
{
This.TitleText = Text;
}
}
And this is the UserControl xaml code
<UserControl x:Class="BindingUserControlTest.TestBind"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}" Height="125.889" Width="227.974">
<Grid x:Name="LayoutRoot" Background="{StaticResource PhoneChromeBrush}">
<TextBlock x:Name="lblTitle" HorizontalAlignment="Left" TextWrapping="Wrap" Text="TextBlock" VerticalAlignment="Top"/>
<TextBlock x:Name="lblDescription" HorizontalAlignment="Left" TextWrapping="Wrap" Text="TextBlock" VerticalAlignment="Top" Margin="0,32,0,0" Width="218" Height="84"/>
</Grid>
And this is the code behind :
private string _ShownTitle;
public string ShownTitle { get { return _ShownTitle; } set { _ShownTitle = value; }}
You need to use DependencyProperties to be able to bind to an attribute. Rather than re-write the code for you, I will give you a link to a blog by Jerry Nixon, who explains the process well.
http://blog.jerrynixon.com/2013/07/solved-two-way-binding-inside-user.html
EDIT:
Code Behind for the Usercontrol would look like.
public sealed partial class ExampleControl : UserControl
{
public static readonly DependencyProperty exampleProperty = DependencyProperty.Register("ExampleData", typeof(Double), typeof(NutritionLabelControl), null);
public ExampleControl()
{
InitializeComponent();
(this.Content as FrameworkElement).DataContext = this;
}
public Double ExampleData
{
get { return (Double)GetValue(exampleProperty); }
set
{
SetValue(exampleProperty, value);
}
}
}
Then in your userControls XAML you would have something like:
<UserControl>
<Grid x:Name="LayoutRoot">
<TextBlock Text="{Binding ExampleData}" />
</Grid>
</UserControl>
You can then use the same Binding format in the MainPage.xaml as in the usercontrols XAML.

Visibility Binding using DependencyProperty

I've some simple code below that uses a ToggleButton.IsChecked property to set the Visibility of a TextBlock. It works fine. Since this doesn't quite fit in with my program's structure, I'm trying to bind the visibility of another TextBlock to a DependencyProperty of "this". It compiles fine, but it produces no effect. I'm doing something wrong, just not sure what.
XAML
<Window x:Class="ToggleButtonTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Width="200" Height="100">
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
</Window.Resources>
<StackPanel>
<ToggleButton x:Name="toggleButton" Content="Toggle"
IsChecked="True" Checked="toggleButton_Checked"/>
<TextBlock Text="Some Text"
Visibility="{Binding IsChecked,
ElementName=toggleButton,
Converter={StaticResource BooleanToVisibilityConverter}}"/>
<TextBlock Text="More Text"
Visibility="{Binding ShowMoreText,
ElementName=this,
Converter={StaticResource BooleanToVisibilityConverter}}"/>
</StackPanel>
</Window>
C#
using System.Windows;
namespace ToggleButtonTest
{
public partial class MainWindow : Window
{
static MainWindow()
{
FrameworkPropertyMetadata meta =
new FrameworkPropertyMetadata(true,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault);
ShowMoreTextProperty =
DependencyProperty.Register("ShowMoreText",
typeof(bool), typeof(MainWindow), meta);
}
public MainWindow()
{
InitializeComponent();
}
public static readonly DependencyProperty ShowMoreTextProperty;
public bool ShowMoreText
{
get
{
return (bool)GetValue(ShowMoreTextProperty);
}
set
{
SetValue(ShowMoreTextProperty, value);
}
}
private void toggleButton_Checked(object sender, RoutedEventArgs e)
{
ShowMoreText = toggleButton.IsChecked.Value;
}
}
}
Edit:
Having had this answered, I want to post my working code...
XAML
<Window x:Class="ToggleButtonTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Width="200" Height="100"
Name="thisWindow">
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
</Window.Resources>
<StackPanel>
<ToggleButton x:Name="toggleButton"
Content="Toggle"
IsChecked="{Binding Path=ShowMoreText, ElementName=thisWindow}"/>
<TextBlock Text="More Text"
Visibility="{Binding Path=ShowMoreText,
ElementName=thisWindow,
Converter={StaticResource BooleanToVisibilityConverter}}"/>
</StackPanel>
</Window>
C#
using System.Windows;
namespace ToggleButtonTest
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public static readonly DependencyProperty ShowMoreTextProperty =
DependencyProperty.Register("ShowMoreText", typeof(bool),
typeof(MainWindow), new FrameworkPropertyMetadata(true,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public bool ShowMoreText
{
get
{
return (bool)GetValue(ShowMoreTextProperty);
}
set
{
SetValue(ShowMoreTextProperty, value);
}
}
}
}
ElementName must really be an element name. this doesn't fly. Fortunately, you do have an element of type MainWindow here with a ShowMoreText property: the root Window element.
Give the Window a name and use that as ElementName, as below:
<Window x:Class="ToggleButtonTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Width="200" Height="100"
x:Name="thisWindow">
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
</Window.Resources>
<StackPanel>
<ToggleButton x:Name="toggleButton" Content="Toggle"
IsChecked="True" Checked="toggleButton_Checked"/>
<TextBlock Text="Some Text"
Visibility="{Binding IsChecked,
ElementName=toggleButton,
Converter={StaticResource BooleanToVisibilityConverter}}"/>
<TextBlock Text="More Text"
Visibility="{Binding ShowMoreText,
ElementName=thisWindow,
Converter={StaticResource BooleanToVisibilityConverter}}"/>
</StackPanel>
</Window>
Note that you can do the same using RelativeSource Self, but I prefer the method above.
The way you have it set up currently won't ever set ShowMoreText to false. The Checked handler will only be called when the ToggleButton's IsChecked changes from false to true. To also go the other way you need a handler for Unchecked as well. The best way to handle this situation would be to instead set a Binding on the ToggleButton that will do both without any event handlers (using Jay's changes):
IsChecked="{Binding Path=ShowMoreText, ElementName=thisWindow}"
Give your Window a name and set the ElementName to that name, instead of using "this".

Categories

Resources