Add XAML programmatically in Windows Phone - c#

I need to add programmatically some UI.
In order to do that I am creating each single Object and adding it to my main grid.
This way (I need to do that in a lambda function):
Deployment.Current.Dispatcher.BeginInvoke(() => {
StackPanel stkpanel = new StackPanel();
stkpanel.Orientation = Orientation.Horizontal;
TextBlock textBlock = new TextBlock();
textBlock.Text = "text1";
Grid myGrid = new Grid();
myGrid.Children.Add(textBlock);
MainPage currentPage = (MainPage)(((App)Application.Current).RootFrame.Content as PhoneApplicationPage);
currentPage.LayoutRoot.Children.Add(myGrid);
...
});
Is there a way to add to my MainPage.xaml another File.xaml and display the result?
I am using silverlight 8.1
Thanks

There some different ways to do that.
UserControl
One way is to create a user Control and use it direct in XAML or instantiate it in the code behind like a standard control. You can find a template in the Add Item dialog in Visual Studio.
Pages
It is also possible to create a second page and Display them in a Frame tag. It is possible in XAML and code behind. Here is an example:
<Frame Margin="0,148,0,0" Name="myFrame"/>
myFrame.Navigate(typeof(BlankPage1));
XamlReader
If you have a partial XAML code, stored in a file you can parse it manual and add the objects to the page. Your XAML must look like the following with the xmlns Attribut
<StackPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<Button Content="BTN 1" />
<Button Content="BTN 2" />
<Button Content="BTN 3" />
</StackPanel>
Then you can parse it with XamlReader:
var uri = new Uri("ms-appx:///test.xaml");
var file = await StorageFile.GetFileFromApplicationUriAsync(uri);
var panel = XamlReader.Load(await FileIO.ReadTextAsync(file)) as StackPanel;
root.Children.Add(panel);
Be sure you set the Build Action of the XAML file to Content, if you will use it as an resource. You can also pass directly a string with XAML code to the XamlReader.Load function.

Related

Adding a new component in c# WPF

I am new to C# and WPF. I wanna add the following element in the page.
<Label HorizontalAlignment="Left" Width="99" MouseMove="Variable_MouseMove" x:Name="VariableLabel">
<Run
Background="red"
Text="Variable"
x:Name="Variable"
/>
</Label>
I wanna add it dynamicly to the Canvas, like that:
Label label = new Label();
Run r = new Run();
label.Children.add(r); // error: label doesn't have the definition of Children
canvas.Children.add(label);
I want to set the background color behind only the text, rather than the whole label, so I use Run as Label child element and set the child element's background.
What should I do? Thanks!
The label don't have Children but in you case you can use Content. So
label.Content = run;
will work

Change DataTemplate for Expander header from Code Behind

I am attempting to add an expander from code behind to an existing ListBox. The content that needs to be displayed in the header of the expander comes from a directory name which may or may not contain underscores. I want to preserve the directory name and not have the first underscore be interpreted as a keyboard shortcut.
I found this thread discussing how to do it in xaml and have tried to implement the same solution in code behind without any luck.
I also found this thread discussing how to create a data template from the code behind, but I can't get that to work either.
I have tried the following code snippets but either it won't compile or it just displays blanks for the expander headers:
String markup = String.Empty;
markup = "<TextBlock text=\"" + directory.Name + "\"/>";
ex.HeaderTemplate = new DataTemplate((DataTemplate)XamlReader.Load(markup));
.
ex.HeaderTemplate = new DataTemplate("TextBlock");
TextBlock tb = new TextBlock();
tb.Text = directory.Name;
ex.Header = tb;
you don't need to change HeaderTemplate to avoid underscore conversion into AccessKey.
add TextBlock into Expander.Header explicitly, and it will keep text unchanged.
<Expander>
<Expander.Header>
<TextBlock x:Name="ExpanderHeader"/>
</Expander.Header>
</Expander>
this way you don't need to create UI elements in c# code.
change header text in ExpanderHeader textBlock
ExpanderHeader.Text = directory.Name;
or bind it if there is a view model:
<TextBlock x:Name="ExpanderHeader" Text="{Binding Path=...}"/>

How to modify Content of Control (create new content and restore old content back later) C#?

I need to modify my XAML elements by code. I need to replace original content with new content inside ScrollViewer "XAML_ScrollViewer". Simple example of XAML code.
<ScrollViewer x:Name="XAML_ScrollViewer">
<ListView x:Name="XAML_ListView">
<ListView.ItemTemplate>
<DataTemplate x:DataType="data:SomeInformation">
<Grid>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ScrollViewer>
To do this I use following code. Everything is so far well. New content appears as it should to do.
C#
//SourceElementParent = XAML_ScrollViewer
//GET PRESENT CONTENT
FrameworkElement ControlOldContent = (SourceElementParent as ContentPresenter).Content as FrameworkElement;
//CREATE NEW GRID
Grid NewParentGrid = new Grid();
//USE NEW GRID AS CONTENT
(SourceElementParent as ContentPresenter).Content = NewParentGrid;
//ADD SOME ELEMENT 01
NewParentGrid.Children.Add(XAMLElement_01);
//ADD SOME ELEMENT 02
NewParentGrid.Children.Add(XAMLElement_02);
//ADD OLD CONTENTS INTO A NEW GRID
NewParentGrid.Children.Add(ControlOldContent );
But when I need to restore the original content I cannot do it. The following code works BUT created Grid inside ScrollViewer must remain.
C#
//CLEAR ALL CHILDREN OF THE GRID
((SourceElementParent as ContentPresenter).Content as Grid).Children.Clear();
//ADD OLD CONTENT TO THE GRID
((SourceElementParent as ContentPresenter).Content as Grid).Children.Add(ControlOldContent);
Because I want to restore the control ScrollViewer to the old state (content as it was before any modify) I also need to rid of the Grid I created earlier. But if I do so I get exception if I resize window size by mouse. If I don't resize all looks good.
I get the following exception:
e = {Windows.UI.Xaml.UnhandledExceptionEventArgs}
Exception = {"Invalid pointer\r\n\r\nInvalid pointer\r\n"}
I tried to use following code to restore contents but it fails.
C#
//CLEAR ALL CHILDREN OF GRID
((SourceElementParent as ContentPresenter).Content as Grid).Children.Clear();
//ADD ORIGINAL CONTENT
(SourceElementParent as ContentPresenter).Content = ControlOldContent;
So, any good hints how to solve this problem?
Found a working solution finally. Content must restore as new ScrollContentPresenter. So the ContentPresenter type must be exactly correct and I didn't realize this before.
//CLEAR ALL CHILDREN OF GRID
((SourceElementParent as ContentPresenter).Content as Grid).Children.Clear();
//RESTORE SCROLLVIEWER CONTENT.
(SourceElementParent as ScrollContentPresenter).Content = new ScrollContentPresenter() { Content = ControlOldContent };

Unable to load XAML through XamlReader

I have a WPF application. Source is here. My requirement is, if the use click the Save button, I want to capture the current window's XAML including inputs and save it to the database. After that in application, somewhere else, I need to load back that window from DB. I tried this using XamlWriter. Its working fine if I didnt name the controls in Xaml. If I add x:Name attribute in Xaml for any control, it gives exception as shown below. Please help.
if you put a Console.WriteLine(window); behind var window = XamlWriter.Save(this); like so:
var window = XamlWriter.Save(this);
Console.WriteLine(window);
you get this printed:
<MainWindow Title="MainWindow" Width="525" Height="350" Visibility="Visible" xmlns="clr-namespace:XamlReaderSample;assembly=XamlReaderSample" xmlns:av="http://schemas.microsoft.com/winfx/2006/xaml/presentation"><av:Grid><av:StackPanel Margin="0,10,0,0">
<av:TextBox Width="100" xml:space="preserve" /><av:TextBox Width="100" Margin="0,10,0,0" xml:space="preserve" />
<av:Button Name="btnSave" Width="80" Margin="0,10,0,0">Save</av:Button>
</av:StackPanel></av:Grid></MainWindow>
within this code there is a button named btnSave. in the rest of your code you try to create a new window from that code. once you try to create that window, you have a 2nd button (besides the one you did click) with that name. and that causes the error.
I tried like this and it worked.
var win = new Window();
win.Name = this.Name;
win.Content = this.Content;
var xaml = XamlWriter.Save(win);
But still I cant do like this if the window have a DataGrid in it, since DataGrid doesnt support serialization. Need to find out some other method.

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.

Categories