How to add variable Image on User Control - c#

I'm trying to have a user control where an image is passed in from its containing element. The purpose is so that I can reuse a common set of visual elements while only changing the image. For example:
The control usage:
<DataTemplate DataType={x:Type myType}>
<local:MyControl PlotIconSource="..\Images\Scatter.png"/>
</DataTemplate>
The Image inside the control
<UserControl x:Class="MyControl">
<Image Source="{Binding PlotIconSource}"/>
</UserControl>
Finally the dependency property for PlotIconSource in the code-behind for MyControl.xaml.cs.
public ImageSource PlotIconSource
{
get { return (ImageSource)GetValue(PlotIconSourceProperty); }
set { SetValue(PlotIconSourceProperty, value); }
}
public static readonly DependencyProperty PlotIconSourceProperty =
DependencyProperty.Register(
"PlotIconSource",
typeof(ImageSource),
typeof(PlotHeader),
new UIPropertyMetadata());
I'm sure I've missed something along the way so any help would be appreciated.

You might want to bind via RelativeSource or with ElementName:
<UserControl x:Class="MyControl" Name="control">
<Image Source="{Binding PlotIconSource, ElementName=control}"/>
</UserControl>
(Do not set the DataContext, it will be invisible from the outside and mess with bindings meant for an inherited DataContext)

Looks right to me, are you getting an error message or something?

Related

Why is the binding on my custom control not working?

I have a MainView where I'm using a component I created called VideoPlayer. VideoPlayer is a Grid that has a MediaElement as well as some buttons and a slider to control video playback.
I'm trying to set the path of the video to be played in MainView by binding a property in VideoPlayer to a property in MainView.
The relevant parts of the code are below:
Snippet from VideoPlayer.xaml:
<MediaElement
Name="MediaElement"
Grid.Row="0"
LoadedBehavior="Manual"
Stretch="Uniform"
Source="{Binding VideoLocation}" />
Snippet from VideoPlayer.xaml.cs:
public string VideoLocation
{
get { return (string)GetValue(VideoLocationProperty); }
set { SetValue(VideoLocationProperty, value); }
}
public static readonly DependencyProperty VideoLocationProperty
= DependencyProperty.Register("VideoLocation", typeof(string), typeof(VideoPlayer), new PropertyMetadata(null));
VideoPlayer is used in MainView.xaml, which is following MVVM:
<ReuseableComponents:VideoPlayer VideoLocation="{Binding VideoPath}"/>
This is the bound property, VideoPath, in MainViewModel.cs:
private string _videoPath;
public string VideoPath
{
get => _videoPath;
set => SetProperty(ref _videoPath, value);
}
If I remove the VideoLocation binding and hard code a path in MainView.xaml, the video will play just fine:
<ReuseableComponents:VideoPlayer VideoLocation="C:\Movies\FightClub.mp4"/>
So, I think the issue is with the MainView binding and not the VideoPlayer binding.
All of my other property bindings in MainView work, and they all follow this pattern:
<ComponentName PropertyName="{Binding ViewModelPropertyName}">
where ViewModelPropertyName is defined in MainView with a backing field and the setter calls SetProperty()
EDIT
The error I made was: in the constructor for VideoPlayer I had a line DataContext = this; I think I saw it in some tutorial somewhere. Anyway as per #Magnus advice I removed the line and changed VideoPlayer.xaml to
<UserControl x:Name="TheControl" ...>
<Grid DataContext={Binding ElementName=TheControl} ...>
...
</Grid>
</UserControl>
Where previously the UserControl didn't have the name property set and the Grid didn't have the DataContext property set.
The problem might be that you are setting the DataContext of your VideoPlayer UserControl to itself on the top level. That means you override the MainView DataContext that the control should inherit, i.e., this binding:
<ReuseableComponents:VideoPlayer VideoLocation="{Binding VideoPath}"/>
tries to bind to the VideoPlayer control instead of the MainView DataContext.
You could instead set the internal DataContext of your VideoPlayer on a contained element, something like this:
<UserControl x:Name="TheControl" ...>
<Grid DataContext={Binding ElementName=TheControl} ...>
...
</Grid>
</UserControl>
The Binding is being done relative to your control's DataContext, which you probably haven't set. You need to set it relative to the control instead. Give your control a name like this:
<UserControl x:Class="YourApp.YourUserControl"
... etc ...
x:Name="_this">
And then bind to that directly:
Source="{Binding ElementName=_this, Path=VideoLocation}"
Alternatively you can also use a RelativeSource binding with AncestorType set to your user control type.

WPF user control properties not binding or updating

I'm messing around with WPF and creating User Controls but having a hard time understanding how the databinding is supposed to work. Data binding seems to be overly complex and as long as WPF has been out I would think MS would've created some shortcuts to prevent having to do so much boilerplate code.
User control xaml
<UserControl x:Class="WPFTest.FancyBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d">
<DockPanel>
<Label Content="{Binding MyText}"></Label>
</DockPanel>
</UserControl>
User control .cs
public partial class FancyBox : UserControl
{
public static readonly DependencyProperty MyTextProperty = DependencyProperty.Register("MyText", typeof(string), typeof(FancyBox), new PropertyMetadata(null));
public string MyText
{
get => (string)GetValue(MyTextProperty);
set => SetValue(MyTextProperty, value);
}
public FancyBox()
{
InitializeComponent();
}
}
Usage in my main window
<StackPanel>
<local:FancyBox MyText="testing!"/>
</StackPanel>
The binding Content="{Binding MyText}" is binding to the DataContext of the control (Label) which is inherited from closest ancestor up the tree who have one (your code doesn't show any DataContext assignment)
Your intended behavior is for Label's Content to bind to the User Control's Property in this case you need to make the user control your source. Many ways to do this for example:
<UserControl x:Class="WPFTest.FancyBox"
x:Name="RootElement"
....
<Label Content="{Binding MyText, Source={x:Reference RootElement} />
Or another way:
<Label Content="{Binding MyText, RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type local:FancyBox}}" />
Keep in mind any Bindings without a source (Source, RelativeSource) will source from DataContext.
I guess I didn't need data binding for this at all
I changed my controls label to:
<Label x:Name="lblText"></Label>
and my code-behind to:
public string MyText
{
get => lblText.Content.ToString();
set => lblText.Content = value;
}

Binding UWP control to code behind property

I am developing UWP app and I created new user control and I want to bind it to dependency property in the control's code behind (without the datacontext).
Code Behind:
public Brush Fill
{
get { return (Brush)GetValue(FillProperty); }
set { SetValue(FillProperty, value); }
}
// Using a DependencyProperty as the backing store for Fill. This enables animation, styling, binding, etc...
public static readonly DependencyProperty FillProperty =
DependencyProperty.Register("Fill", typeof(Brush), typeof(MyControl), new PropertyMetadata(new SolidColorBrush(Colors.Black)));
XAML:
...
<Grid>
<Path Fill="{Binding ????}" Stretch="Fill">
...
</Path>
</Grid>
I want that my path's fill property will bind to the property Fill from code behind (the data context should hold different data so I can't use it here)
How can I do that in UWP?
x:Bind would work perfectly on this. Note x:Bind will be looking for properties, methods & events defined in your XAML's code-behind. It's a more performant binding than ElementName.
<Path Fill="{x:Bind Fill, Mode=OneWay}" />
You should be able to use the ElementName property of a binding to circumvent the data context, just as normal WPF allows you to do.
If the property is part of the user control you'll need to assign a name via x:Name to your user control in xaml to access it
<UserControl [...] x:Name="Control"...`
Then use something like{Binding ElementName=Control, Path=Fill}, as long as Fill is a property of your user control.

WPF XAML Binding not calling changed event

My program has a custom DependencyObject to which I bind values of another DependencyObject which are set in code:
<TabControl
Grid.Column="1"
Grid.Row="1">
<TabItem
Header="XML">
<TextBox
Text="{Binding Asset.Xml, ElementName=window}"
IsReadOnly="True" />
</TabItem>
<TabItem
Header="Texture">
<we:DXImage>
<we:DXImage.Renderer>
<we:TextureRenderer
Source="{Binding Asset.Image, ElementName=window}" />
</we:DXImage.Renderer>
</we:DXImage>
</TabItem>
</TabControl>
The TextBox binding to Asset.Xml works flawlessly, also if I replace the xaml of the second item with a TextBox it also displays the content of Asset.Image (a path to an image of type string).
The Source Property of the renderer looks like this:
private static readonly DependencyProperty SourceProperty = DependencyProperty.Register("Source", typeof(string), typeof(TextureRenderer),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender, SourceChanged));
public string Source
{
get { return (string)GetValue(SourceProperty); }
set { SetValue(SourceProperty, value); }
}
private static void SourceChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
// Do stuff
}
However the SourceChanged event is never called.
I have updated the project on GitHub:
https://github.com/Qibbi/WrathEd/tree/master/WrathEd2
the xaml code is located in the WrathEd2 project while the DXImage, Renderer, and other support classes are in WrathEd.Windows
The current MainWindow is a mess code behind wise atm, I plan to refactor it into appropriate parts when finishing the project.
The problem is that your we:TextureRenderer is not a part of the VisualTree (as it is inside a property). Therefore, the binding cannot find the source Element.
According to ElementName Binding is failing, you can use
Source={x:Reference window}
instead of ElementName=window.
According to the MSDN documentation here
You need to set NotifyOnSourceUpdated to true for your binding

Setting Image Source in a UserControl Button

i got an userControl, and i want to set the image button from the page in which userControl is included.
If i set my button image in the usercontrol in this way it works but i can't change.
<Button blablabla>
<Image Source="../../../Assets/truck-512.png"/>
</Button>
So i define a property from the codebehind
public ImageSource ButtonSource
{
get { return (ImageSource)GetValue(SourceProperty); }
set { SetValue(SourceProperty, value); }
}
// Using a DependencyProperty as the backing store for Source. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SourceProperty =
DependencyProperty.Register("Source", typeof(ImageSource), typeof(TruckFormUC),null);
and in my userContol xaml i set my image as following
<Image x:Name="imgIcon" Source="{Binding Path=ButtonSource}"/>
than i use my userControl in my page and i try to set the image source, but it doesn't work
<UC:TruckFormUC x:Name="truckForm"
ButtonSource="../../../Assets/truck-512.png"/>
Thanks a lot
The userControl and the page are in the same folder.
Your binding is looking for ButtonSource property on the DataContext object. If you want to bind to your UserControl you should use relative binding.
<Image x:Name="imgIcon" Source="{Binding
RelativeSource={RelativeSource FindAncestor, AncestorType=UC:TruckFormUC},
Path=ButtonSource}"/>
See RelativeSource markup extension MSDN article for more info
You should also be able to use element binding.
<Image x:Name="imgIcon" Source="{Binding ElementName=truckForm, Path=ButtonSource}"/>

Categories