why content template of button does not have object? - c#

i am just exploring windows phone runtime apps template. But i am seeing a weird thing.
I have Button defined in Xaml with ContentTemplate set in it. I wanted extract the Image control defined in the ContentTemplate of this button. But it is coming null.
Xaml code :-
<Button x:Name="PlayButton" Click="PlayButton_OnClick">
<Button.ContentTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Name="Panel">
<Image x:Name="ControlImg"
Width="100"
/>
<TextBlock Text="text block" />
</StackPanel>
</DataTemplate>
</Button.ContentTemplate>
Here is button Click event :-
private async void PlayButton_OnClick(object sender, RoutedEventArgs e)
{
var btn = sender as Button;
var ct = btn.ContentTemplate; // this part is also not showing controls in it when expending ct at runtime.
var img = btn.FindName("ControlImg") as Image; // coming null
var stckpnl = btn.FindName("Panel") as StackPanel;// coming null
}
Can anybody check this out why is this happening ?
Edit :- I have broken my problem and reach this very bottom simple level and after seeing this i am just not getting why is this happening ?

That is strange behavior. It should have stack Panel and image in control template. As a work around you can use ContentTemplateRoot to get the image and stackpanel. I have test this, it is working.
((StackPanel)btn.ContentTemplateRoot).Children[0] // image
Hope this helps
Edit:
For Details about why FindName is not working see the the Remarks section on on MSDN
. Here is some relevant quotes
Important In order to use the FindName method effectively, you should understand the concept of a XAML namescope, and how a XAML namescope is created at XAML load time and then referenced and possibly modified at run time. For more info see XAML namescopes.
The most common usage of FindName in your Windows Runtime code will be from within the generated InitializeComponent call for a XAML page. In this situation, FindName is invoked only after the XAML page is loaded. InitializeComponent provides the infrastructure such that any object that was instantiated by XAML loading can conveniently be accessed by your code-behind code. You can then reference the objects as a variable that shares the same name as the markup-declared x:Name.
A run-time API such as FindName is working against a run-time object tree of the app as it exists in memory. When part of this object tree is created from templates or run-time loaded XAML, a XAML namescope is typically not contiguous within that object tree. The result is that there might be a named object in the object tree that a given FindName scope cannot find. The discontinuities between XAML namescopes that you might encounter in typical application scenarios are when objects are created by applying a template, or when objects are created by a call to XamlReader.Load and subsequently added to the object tree.
As you are using DataTemplate so the xaml object tree is not contiguous so that is why FindName is failed to find the control from the xaml tree.
hope this explains...

Related

How to use Windows 10 style resource in WPF with XAML Islands

I'm using XAML Islands to make my app and I want to use Windows 10 styling in my WPF app like here. For example <TextBlock Text="Header" Style="{StaticResource HeaderTextBlockStyle}"/> would result in:
But this doesn't work in WPF (It does work in UWP without any modifications), my understanding is that XAML Islands should make it possible. When I try to simply add the code above to my xaml file I get the exception
Cannot find resource named 'HeaderTextBlockStyle'. Resource names are case sensitive.
I get the same exception if I add Style="{StaticResource HeaderTextBlockStyle}" to a <xamlhost:WindowsXamlHost> element.
So I tried to add the controls with code so I added this WindowsXamlHost control as a stackpanel:
<xamlhost:WindowsXamlHost InitialTypeName="Windows.UI.Xaml.Controls.StackPanel" ChildChanged="WindowsXamlHost_ChildChanged"/>
And added this method (an event handler that is ran when the control is made. Learned it from this) that handles adding additional controls (a TextBlock) to the StackPanel:
private void WindowsXamlHost_ChildChanged(object sender, EventArgs e)
{
// Get the host control
WindowsXamlHost host = (WindowsXamlHost)sender;
// Get the StackPanel in the host
Windows.UI.Xaml.Controls.StackPanel sp = (Windows.UI.Xaml.Controls.StackPanel)host.Child;
// Make a TextBlock to add to the StackPanel
Windows.UI.Xaml.Controls.TextBlock textBlock = new Windows.UI.Xaml.Controls.TextBlock();
// Set the text of the TextBlock
textBlock.Text = "LockCursorInMonitor";
// Get the style resources, cast them to the appropriate type for XAML Islands and add them to the TextBlock
textBlock.Style = (Windows.UI.Xaml.Style)Application.Current.Resources["HeaderTextBlockStyle"];
// Another way to get resources but this doesn't work too.
//textBlock.Style = (Windows.UI.Xaml.Style)this.FindResource("HeaderTextBlockStyle");
// Add the TextBlock to the stackpanel
sp.Children.Add(textBlock);
}
The Application.Current.Resources["HeaderTextBlockStyle"] way does nothing and doesn't throw an exception.
The this.FindResource("HeaderTextBlockStyle") way throws the next exception:
System.Windows.ResourceReferenceKeyNotFoundException: ''HeaderTextBlockStyle' resource not found.'
So how can I get these style resources in my WPF app?
One way to achieve this is to use the package ModernWPF but then you lose all the benefits of XAML Islands (if there are any. Everything I needed from XAML Islands is in ModernWPF and is easier to implement).
After installing and setting ModernWPF up you can simply use the <TextBlock Text="Header" Style="{StaticResource HeaderTextBlockStyle}"/> way and it just works.

Xamarin Forms - LoadFromXaml and referencing containing page

I have a ListView that's on a ContentPage, and I want the ItemTemplate of the ListView to be user-definable - i.e. parsing the XAML for the template at runtime. However, I need to give the user the option of having a button that invokes a command on the page. See below XAML;
<SwipeView>
<SwipeView.RightItems>
<SwipeItems>
<SwipeItem
Text = ""Delete""
BackgroundColor=""Red""
Command=""{Binding Path=ItemDeletedCommand, Source={x:Reference multiEntryPage}}""
CommandParameter=""{Binding .}"" />
</SwipeItems>
</SwipeView.RightItems>
<SwipeView.Content>
...
</SwipeView.Content>
And my content page has the x:Name="multiEntryPage" attribute in its ContentPage element. The code I use to parse the XAML is shown here:
listView.ItemTemplate = new DataTemplate(() => new ViewCell().LoadFromXaml(xaml));
However, this gives the below error:
{Xamarin.Forms.Xaml.XamlParseException: Position 11:33. Can not find
the object referenced by multiEntryPage at
Xamarin.Forms.Xaml.ApplyPropertiesVisitor.ProvideValue (System.Object&
value, Xamarin.Forms.Xaml.ElementNode node, System.Object source,
Xamarin.Forms.Xaml.XmlName propertyName)...
So I think it's fairly clear that the error is happening because the SwipeView XAML doesn't know about its parent at this stage, but I'm at a loss of how to resolve this. Any ideas?
Thanks!
You can create DataTemplate in following ways
Creating an Inline DataTemplate
Creating a DataTemplate with a Type
Creating a DataTemplate as a Resource
Refer https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/templates/data-templates/creating.

WPF complex binding for custom control with sub-class as settings

Right now I am trying to implement a custom control (which is of course way more complex than the demo solution I attached), and I kind of failed at passing a value down via binding.
First of all, the structure of the control:
Control (this is basically a wrapper which passes down properties and handles common functionalities)
Settings (this is a dependency property of the control and is a custom class as well)
Text (this is a dependency property of the settings class)
_renderer (this is a private field of the control which is responsible for rendering the control => in my real control it's just rendering a part of it, but for this example it's enough to just do it like that).
The goal of this is to pass the text from a control like a textbox or something down to the renderer which gets initialized with a reference to the settings.
The XAML which uses the control is written as follows:
<TextBox Text="Initial Text"
x:Name="TextSource"
Grid.Row="0" />
<local:CustomControl Grid.Row="1">
<local:CustomControl.Settings>
<local:CustomControlSettings Text="{Binding Path=Text, ElementName=TextSource, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
</local:CustomControl.Settings>
</local:CustomControl>
When I use the same XAML, but give the settings a fixed value for the "Text" then everything works as expected, but as soon as I change it to a binding, I don't even get the initial value any more.
Code with is passing down the text:
Passing the settings to the renderer
private void OnLoaded(object sender, RoutedEventArgs e)
{
_renderer = new ControlRenderer();
_renderer.Initialize(_renderArea, Settings);
}
binding the settings text to the renderers internal text dependency property
SetBinding(TextProperty, new Binding{Path = new PropertyPath(CustomControlSettings.TextProperty), Source = settings});
Note: you can uncomment this without any effect, so this should not be the problem if you ask me.
And hereĀ“s a link to the demo solution I created.
After four hours of pain, later I found the solution. The problem is that the settings-object is not a part of the visual tree and therefore the dependency properties are not resolved.
So to be able to do things like this you need to add the settings to the visual tree. I did that by adding it to the canvas children inside of the CustomControl.

Create an element from template programmatically

I'd like to move an element from one grid into another and have a problem to assign programmatically a template to the new instance. Further, details of my attempt.
For this purpose, I create an instance of the class together with its visual appearance from the template.
Inside the Window tag I declare the namespace:
xlmns:my="clr-namespace:myNameSpace"
I have a template in resources:
<ControlTemplate x:Key="templateX">
<StackPanel>
<Image Source="pic.png" Width="50" Height="50"/>
</StackPanel>
</ControlTemplate>
and place the element into the grid.
<Grid Grid.Row="2">
<StackPanel>
<my:someClass Template="{StaticResource templateX}" MouseMove="_event">
</StackPanel>
</Grid>
Now, I drag the element, the event "_event" fires. If I push a standard element (e.g. Rectangle) through this, I do the following at the end of the drag-n-drop chain of events:
Rectangle new_instance = new Rectangle();
// place for rectangle's form and color
NewPlace.Children.Add(new_instance);
// place for positioning the rectangle in NewPlace canvas
How, can I do the last part with the element of someClass? If I do
someClass new_instance = new someClass();
NewPlace.Children.Add(new_instance);
the template "templateX" isn't assigned to it.
The issue in this case seems to be that you want to combine two things:
an instance of your custom class (new_instance)
a control template available as a XAML resource
You already know how to create the instance of your class and how to add it to the Children list.
How to retrieve the control template (or for that matter, any other object) from a XAML resource has been discussed in other SO questions, e.g.:
How can I access ResourceDictionary in wpf from C# code?
Accessing a resource via codebehind in WPF
This leads to:
ControlTemplate template = (ControlTemplate)this.FindResource("templateX");
Now, the crucial point is that you do not want to add the control template itself to the Children list. The control template is just a set of instructions how to create a UI tree for your control and bind its properties to those of your control, where appropriate.
Instead, you want to configure new_instance to use the control template you retrieved from the resource. You can do that by assigning the control template to the Template property of new_instance:
new_instance.Template = template;
Once new_instance is added to Children, it will be displayed and it will use your custom control template.

How to get an icon in stackPanel in a XAML file

I am new to XAML and C#
I have an icon created already in a project and and I have to use this icon whenever I select one of the option from the dropdown menu.
I made a stackpanel in XAML file
<StackPanel Name="stackPanelforIcon">
</StackPanel>
In the code behind file I have different cases for the dropdown menu.
case IconOnSelect:
?????? = IconList.NewIcon;
This NewIcon is the one already created and I am using the source also for this
using IconProject.Iconlists;
On writing IconList.NewIcon I am not getting any error, it is referenced correctly.
What should I write at ?????? to reference it. Is there any other way apart from using stackPanel to include an icon
A StackPanel cannot show an icon on it's own. You need a control for it, for example an Image.
<StackPanel Name="stackPanelforIcon">
<Image x:Name=theImage" />
</StackPanel>
Then you can use your Icon in your code behind like this:
this.theImage.Source = IconList.NewIcon;
You may need to convert your value, you never said what type it actually is.
Please note that using code-behind is not the preferred way with WPF. Using MVVM is way easier and more natural working with WPF, using code-behind you will fight WPF all the way. Using MVVM, this could be:
<StackPanel Name="stackPanelforIcon">
<Image Source="{Binding CurrentImage}" />
</StackPanel>
with your ViewModel having a property called CurrentImage that you would set when you want to change it. Don't forget to implement INotifyPropertyChanged for the changes to take effect though.

Categories