I have a UserControl that has an Image and a Textblock, like so:
<Image Stretch="UniformToFill" Source="{Binding Path=ImageURL}"/>
<TextBlock Text="{Binding Path=Title}"/>
in the .cs of the user control i have Dependency properties to make the binding:
public string Title
{
get { return (string)GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register("Title", typeof(string), typeof(MyUserControl), null);
public string ImageURL
{
get { return (string)GetValue(ImageURLProperty); }
set { SetValue(ImageURLProperty, value); }
}
public static readonly DependencyProperty ImageURLProperty =
DependencyProperty.Register("ImageURL", typeof(string), typeof(MyUserControl), null);
public MyUserControl()
{
this.InitializeComponent();
(this.Content as FrameworkElement).DataContext = this;
}
In my MainPage.xaml i call it inside a list
<ListView Grid.Row="1"
HorizontalAlignment="Center"
Name="ControlsListView">
<ListView.ItemTemplate>
<DataTemplate>
<controls:MyUserControl Margin="20"
Title="{Binding Title}" //works
ImageURL="{Binding ImageURL}"/> //doesn't work
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
And then i get this Data from Json.net and feed it to the ItemsSource of the ListView. It works just for the Title but no for the ImageURL, anyone could help me why?
Probably a bad DataContext, see ReSharper WPF error: "Cannot resolve symbol "MyVariable" due to unknown DataContext". The answer describes how to use the free Snoop utility to detect runtime binding errors.
By default, the DataContext for a UserControl is not set to point at the code behind.
Add this:
<UserControl DataContext="{Binding RelativeSource={RelativeSource Self}}">
Perhaps the backslashes in your ImageUrl are confusing things.
What ImageUrl are you using? Json might be interpreting the backslash as an escape character, and you might have to encode the ImageUrl using a double backslash.
Related
This question is similar to Windows Phone 8.1 Toggling the visibility of a TextBlock in a DataTemplate
and countless others, but none of these ideas are working. The Loaded event is never triggered after I add my Textblock to my datatemplate in my hub. The Visual Tree search is not finding my TextBlock.
I have tried a basic binding like this:
<HubSection Background="{StaticResource HubSectionBackgroundBrush}"
MaxWidth="{x:Bind DesiredHubSectionWidth, Mode=OneWay}"
Header="You have selected:" Padding="60"
>
<DataTemplate x:DataType="local:Scenario4">
<TextBlock TextWrapping="Wrap" Style="{StaticResource BasicTextStyle}" HorizontalAlignment="Left" Text="{Binding Item}"/>
</DataTemplate>
</HubSection>
with
public string Item { get; set; }
Item = makeText.Text;
But this doesn't work (Text on the Hub is always empty). From looking at previous posts and code I have come up with this xaml code using Dependency Properties:
<HubSection Background="{StaticResource HubSectionBackgroundBrush}"
MaxWidth="{x:Bind DesiredHubSectionWidth, Mode=OneWay}"
Header="You have selected:" Padding="60"
>
<DataTemplate x:DataType="local:Scenario4">
<TextBlock TextWrapping="Wrap" Style="{StaticResource BasicTextStyle}" HorizontalAlignment="Left" Text="{x:Bind DesiredSelectionText, Mode=OneWay}"/>
</DataTemplate>
</HubSection>
with this in the c#
private static DependencyProperty s_desiredHubSectionWidthProperty
= DependencyProperty.Register("DesiredHubSectionWidth", typeof(double), typeof(Scenario4), new PropertyMetadata(560.0));
private static DependencyProperty selectionText = DependencyProperty.Register("SelectionText", typeof(string), typeof(Scenario4), new PropertyMetadata("Nothing"));
public static DependencyProperty DesiredHubSectionWidthProperty
{
get { return s_desiredHubSectionWidthProperty; }
}
public static DependencyProperty DesiredSelectionTextProperty
{
get { return selectionText; }
}
public string DesiredSelectionText
{
get { return (string)GetValue(selectionText); }
set { SetValue(selectionText, value); }
}
public double DesiredHubSectionWidth
{
get { return (double)GetValue(s_desiredHubSectionWidthProperty); }
set { SetValue(s_desiredHubSectionWidthProperty, value); }
}
and I set the text with
DesiredSelectionText = makeText.Text;
The width binding works perfectly, but the text is not updating. What is the proper way to change Hub/DataTemplate Text at Runtime? Since the Hub is not even printing "Nothing", something must be really wrong.
As a last resort I am thinking I will just construct my own datatemplate and assign it at runtime, but the only code I can find for that is deprecated(uses FrameworkElementFactory).
I was trying to assign to the textblock in the OnNavigatedTo method which is called before the textblock's Loaded method. That's why my program was showing the textblock as null.
Turns out the link I posted is the solution, just for me, the textblock was loaded after the page was navigated to.
Here is the XAML for my user control:
<UserControl x:Name="titledTextBox" x:Class="VitalStats.View.Controls.TitledTextBox"
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}"
d:DesignHeight="480" d:DesignWidth="480"
>
<Grid x:Name="LayoutRoot">
<StackPanel Orientation="Vertical">
<TextBlock x:Name="titleTextBlock"
TextWrapping="Wrap"
Margin="12,5,0,-5"
Text="{Binding Title, ElementName=titledTextBox, FallbackValue=Title Here}"
FontSize="20"
Foreground="{StaticResource PhoneSubtleBrush}"/>
<TextBox x:Name="inputTextBox" Text="{Binding Text, ElementName=titledTextBox, Mode=TwoWay}"/>
</StackPanel>
</Grid>
</UserControl>
and this is what my code-behind looks like,
// Usings here
namespace VitalStats.View.Controls
{
public partial class TitledTextBox : UserControl
{
[Description("A TextBox with built in title")]
public TitledTextBox()
{
InitializeComponent();
if (DesignerProperties.GetIsInDesignMode(this) )
{
this.Title = "Title Here";
}
}
public string Title
{
get { return this.GetValue(TitleProperty) as string; }
set { this.SetValue(TitleProperty, value); }
}
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register("Title", typeof(string), typeof(TitledTextBox), null);
public string Text
{
get { return this.GetValue(TextProperty) as string; }
set { this.SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(TitledTextBox), null);
}
}
The binding work when reading data into the UI (so the Title property works OK) however
when reading from th UI (i.e. trying to access Text from the code) the property is always null implying that the binding is only one-way (despite the Mode=TwoWay property).
I am aware (thanks to XamlZealot's answer) of the FindAncestor binding, however AncestorType does not exist in Windows Phone 7 (or Silverlight) XAML namespace.
How then do I set up a two-way binding to a UserControl property from within the UserControl?
This is on Windows Phone 7 (7.1 project).
Try a RelativeSource Binding in lieu of the ElementName:
Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UserControl}, Path=Title, FallbackValue=Title Here}"
So the following is perhaps not what I asked but what I used to solve the problem. Since all I wanted to do was link the text of the title TextBlock to Title on the UserControl and the same for the text in the input, the code behind can look like this,
public string Title
{
get { return this.titleTextBlock.Text; }
set { this.titleTextBlock.Text = value; }
}
public string Text
{
get { return this.inputTextBox.Text; }
set { this.inputTextBox.Text = value; }
}
No need for DependencyProperties etc.
I have a custom usercontrol with DataContext="{Binding RelativeSource={RelativeSource self}}"
On the code behind i've made a dependency property like:
public static DependencyProperty ElementNameProperty = DependencyProperty.Register("ElementName",
typeof(string),
typeof(ElementControl),
new PropertyMetadata(new PropertyChangedCallback((s, e) => { new Base().OnPropertyChanged("ElementName"); })));
public string ElementName
{
get
{
return (string)base.GetValue(ElementNameProperty);
}
set
{
base.SetValue(ElementNameProperty, value);
}
}
Now when I try to use this usercontrol in my mainpage.xaml and use the following binding: <test.TestControl ElementName="{Binding name}" />, it keeps searching for 'name' property in my custom usercontrol instead of where it should come from?
What am I doing wrong ?
It searches there because you have the DataContext set on the topmost level for your user control. What you would need to do is get rid of the relative binding to self in the user control and specify ElementName in bindings (inside user control). Btw you probably don't need OnPropertyChanged in the PropertyChangedCallback cause DependencyProperties in their nature notify about value changes.
I eventually solved it this way. Not the way I wanted, but it's a (in my eyes) pretty neat solution.
CustomUserControl.xaml
<UserControl x:Class="TestApp.Controls.CustomUserControl"
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"
Width="75"
Height="75">
<Canvas x:Name="LayoutRoot"
Background="Black">
<StackPanel Orientation="Vertical">
<Image x:Name="UCImage"
Width="50"
Height="50"
HorizontalAlignment="Center" />
<TextBlock x:Name="UCText"
HorizontalAlignment="Center" />
</StackPanel>
</Canvas>
</UserControl>
CustomUserControl.xaml.cs
public partial class ElementControl : UserControl
{
#region DependencyProperty ElementNameProperty
public static DependencyProperty ElementNameProperty = DependencyProperty.Register("ElementName",
typeof(string),
typeof(ElementControl),
new PropertyMetadata(new PropertyChangedCallback((s, e) =>
{
//See Here
((ElementControl)s).UCText.Text = e.NewValue as string;
})));
public string ElementName
{
get
{
return (string)base.GetValue(ElementNameProperty);
}
set
{
base.SetValue(ElementNameProperty, value);
}
}
#endregion
}
A UserControl has 3 Dependency Properties: FormatAvailabilities, Orientation and FullText. FormatAvailabilities is bound to the ItemsSource property of an ItemsControl. Orientation is bound to the Orientation property if the StackPanel which is in the ItemsPanelTemplate within the ItemsControl. FullText is bound to the Visibility property of two TextBlocks inside the DataTemplate of the ItemsControl. I am using two converters to determine which TextBlock to show: a BoolToVisibilityConverter and a BoolToInvertedVisibilityConverter (the latter is an inversion of the former). I copied the Visibility property as-is from the TextBlock (both of them, independently) to the ItemsControl and it works correctly..
It seems that the bindings on the TextBlocks are not working properly because both are always visible. Since they are both binding on the same property but one is inverted, there should never be a possibility for both to be visible at the same time.
I put a breakpoint in my converter and it is never hit, so my guess is that there is an issue with binding from within a repeating control to the outer control in which it is housed.
App.xaml:
<common:BaseApp x:Class="xyz.App" xmlns:converters="clr-namespace:xyz.Converters;assembly=xyz">
<common:BaseApp.RootVisual>
<phone:PhoneApplicationFrame x:Name="RootFrame" Source="/Home.xaml"/>
</common:BaseApp.RootVisual>
<common:BaseApp.Resources>
<converters:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
<converters:BoolToVisibilityConverter x:Key="BoolToInvertedVisibilityConverter" IfTrue="Collapsed" IfFalse="Visible"/>
</common:BaseApp.Resources>
</common:BaseApp>
UserControl XAML:
<UserControl
x:Name="FormatsControl"
x:Class="xyz.Formats"
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}"
d:DesignHeight="480" d:DesignWidth="480">
<ItemsControl Background="Transparent" ItemsSource="{Binding ElementName=FormatsControl, Path=FormatAvailabilities}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="{Binding ElementName=FormatsControl, Path=Orientation}"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding BindsDirectlyToSource=True}" Margin="0,0,10,0" Visibility="{Binding ElementName=FormatsControl, Path=FullText, Converter={StaticResource BoolToVisibilityConverter}}"/>
<TextBlock Text="{Binding Description}" Margin="0,0,10,0" Visibility="{Binding ElementName=FormatsControl, Path=FullText, Converter={StaticResource BoolToInvertedVisibilityConverter}}"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</UserControl>
UserControl CS:
namespace xyz
{
public partial class Formats : UserControl
{
public static readonly DependencyProperty FormatAvailabilitiesDependencyProperty = DependencyProperty.Register("FormatAvailabilities", typeof(FormatAvailability[]), typeof(Formats), null);
public static readonly DependencyProperty OrientationDependencyProperty = DependencyProperty.Register("Orientation", typeof(Orientation), typeof(Formats), new PropertyMetadata(Orientation.Horizontal));
public static readonly DependencyProperty FullTextDependencyProperty = DependencyProperty.Register("FullText", typeof(bool), typeof(Formats), null);
public FormatAvailability[] FormatAvailabilities
{
get { return (FormatAvailability[])base.GetValue(Formats.FormatAvailabilitiesDependencyProperty); }
set { base.SetValue(Formats.FormatAvailabilitiesDependencyProperty, value); }
}
public Orientation Orientation
{
get { return (Orientation)base.GetValue(Formats.OrientationDependencyProperty); }
set { base.SetValue(Formats.OrientationDependencyProperty, value); }
}
public bool FullText
{
get { return (bool)base.GetValue(Formats.FullTextDependencyProperty); }
set { base.SetValue(Formats.FullTextDependencyProperty, value); }
}
public Formats()
{
InitializeComponent();
}
}
}
I must be over looking something...thanks!
There is an issue with naming UserControls in Silverlight 3 as described by this blog post, which is also present in the Windows Phone 7 version of Silverlight. Effectively, if you give the UserControl a name in the XAML where it is used (i.e. it's parent), then that overrides the name given in the UserControl's own XAML file.
I ran into a similar problem, instead of binding to the elementname I changed the binding to this
Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}
And that works just fine.
Looks like you are missing the OnPropertyChanged handler.
Here is one of my dependency properties. Note the changed handler.
public ObservableCollection<ObjWithDesc> ItemsSource
{
get
{
return (ObservableCollection<ObjWithDesc>)GetValue(ItemsSourceProperty);
}
set
{
SetValue(ItemsSourceProperty, value);
}
}
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register(
"ItemsSource",
typeof(ObservableCollection<ObjWithDesc>),
typeof(HorizontalListBox),
new PropertyMetadata(OnItemsSourcePropertyChanged)
);
static void OnItemsSourcePropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
((HorizontalListBox) obj).OnItemsSourcePropertyChanged(e);
}
private void OnItemsSourcePropertyChanged(DependencyPropertyChangedEventArgs e)
{
ObservableCollection<ObjWithDesc> objWithDescList = (ObservableCollection<ObjWithDesc>)e.NewValue;
MainListBox.ItemsSource = objWithDescList;
}
How can I access the public variable which in Sample.xaml.cs file like asp.net <%=VariableName%>?
There are a few ways to do this.
Add your variable as a resource from codebehind:
myWindow.Resources.Add("myResourceKey", myVariable);
Then you can access it from XAML:
<TextBlock Text="{StaticResource myResourceKey}"/>
If you have to add it after the XAML gets parsed, you can use a DynamicResource above instead of StaticResource.
Make the variable a property of something in your XAML. Usually this works through the DataContext:
myWindow.DataContext = myVariable;
or
myWindow.MyProperty = myVariable;
After this, anything in your XAML can access it through a Binding:
<TextBlock Text="{Binding Path=PropertyOfMyVariable}"/>
or
<TextBlock Text="{Binding ElementName=myWindow, Path=MyProperty}"/>
For binding, if DataContext is not in use, you can simply add this to the constructor of the code behind:
this.DataContext = this;
Using this, every property in the code becomes accessible to binding:
<TextBlock Text="{Binding PropertyName}"/>
Another way is to just give a name to the root element of the XAML:
x:Name="root"
Since the XAML is compiled as a partial class of the code-behind, we can access every property by name:
<TextBlock Text="{Binding ElementName="root" Path=PropertyName}"/>
Note: access is only available to properties; not to fields. set; and get; or {Binding Mode = OneWay} are necessary. If OneWay binding is used, the underlying data should implement INotifyPropertyChanged.
For quick-and-dirty Windows in WPF, I prefer binding the DataContext of the Window to the window itself; this can all be done in XAML.
Window1.xaml
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{Binding RelativeSource={RelativeSource self}}"
Title="Window1" Height="300" Width="300">
<StackPanel>
<TextBlock Text="{Binding Path=MyProperty1}" />
<TextBlock Text="{Binding Path=MyProperty2}" />
<Button Content="Set Property Values" Click="Button_Click" />
</StackPanel>
</Window>
Window1.xaml.cs
public partial class Window1 : Window
{
public static readonly DependencyProperty MyProperty2Property =
DependencyProperty.Register("MyProperty2", typeof(string), typeof(Window1), new UIPropertyMetadata(string.Empty));
public static readonly DependencyProperty MyProperty1Property =
DependencyProperty.Register("MyProperty1", typeof(string), typeof(Window1), new UIPropertyMetadata(string.Empty));
public Window1()
{
InitializeComponent();
}
public string MyProperty1
{
get { return (string)GetValue(MyProperty1Property); }
set { SetValue(MyProperty1Property, value); }
}
public string MyProperty2
{
get { return (string)GetValue(MyProperty2Property); }
set { SetValue(MyProperty2Property, value); }
}
private void Button_Click(object sender, RoutedEventArgs e)
{
// Set MyProperty1 and 2
this.MyProperty1 = "Hello";
this.MyProperty2 = "World";
}
}
In the above example, note the binding used in the DataContext property on the Window, this says "Set your data context to yourself". The two text blocks are bound to MyProperty1 and MyProperty2, the event handler for the button will set these values, which will automatically propagate to the Text property of the two TextBlocks as the properties are Dependency Properties.
It is also worth noting that a 'Binding' can only be set on a DependencyProperty of a DependencyObject. If you want to set a non DependencyProperty (eg. a normal property) on an object in XAML, then you will have to use Robert's first method of using resources in the code behind.
myWindow.xaml
<Window
...
<TextBlock Text="{ Binding Path=testString }" />
</Window>
myWindow.xaml.cs
public partial class myWindow: Window
{
public string testString { get; set; } = "This is a test string";
public myWindow()
{
DataContext = this;
InitializeComponent();
}
}
Important
Set Datacontext
testString MUST be public
testString MUST be a property (have a get and set)