In my UWP app I want to create a Page object that I can print. Currently all I have is a list of FrameworkElement (WebViewBrush). Is there some way I can make a Page, whose only feature is a FrameworkElement?
I'm thinking something like:
Page newPage = new Page();
newPage.Add(frameworkElement);
That doesn't work, but that's the sort of thing I'm looking for. I can't see anything on https://msdn.microsoft.com/library/windows/apps/br227503 which demonstrates this but I might be looking for the wrong thing... any advice appreciated, thank you.
EDIT
EDIT 2
A Page can only contain a single child. If you only want a single FrameworkElement displayed in the Page then you can do:
newPage.Content = frameworkElement;
Otherwise you will need to create a container that can contain multiple children:
StackPanel panel = new StackPanel();
panel.Children.Add(frameworkElement);
// Add your many other children.
newPage.Content = panel;
There are of course many other types of container that you can use.
Update
Just to summarise the eventual solution. Although it might not be ideal it was settled on generating a second pageList due to the fact that cloning through VisualBrush is not an option in UWP. In theory the brushes should not be large and shouldn't take up too much memory.
Related
Ok, this is going to be a 1000ft long question, but there's a lot to cover so here goes:
I am creating a paged items control, the purpose of which is to display very large collections in a paged format. I've created a repository on GitHub which can be found here. I have removed any styling for simplicity.
Upon starting the application, it looks like this:
This is pretty straightforward really, there's navigation buttons, an items per page selector but that isn't really important. The problem here is when you click the button "Open New Window".
This will open a new MainWindow, but on the first window, the collection disappears, as shown below:
The image above shows the old window in front, as you can see, there is no list of content as there is on the new window.
So, after smashing my head against a wall for a couple of hours, I am in need of assistance. I'll provide an overview of how the project is structured.
AnagramPagedItemsControl
The control being used for displaying the content is a custom control called AnagramPagedItemsControl, it is responsible for handling navigation between pages. I think the key property here is the PagedCollection.
The PagedCollection dependency property holds the collection which is bound to the Models property in the TestItemsViewModel.
TestItemsViewModel
This is the DataContext of the MainWindow, each window instance should create it's own view model. The CreateTestItems() method is responsible for creating the list of test items.
LazyPagedCollection
The purpose of this collection is to encapsulate the logic of a paged observable collection, it only loads pages when they are needed, hence the laziness.
It exposes methods like NextPage which are called in the AnagramPagedItemsControl when the user clicks on the various navigation buttons. The view model can also call navigation on the LazyPagedCollection, this allows the view model to call navigation without having to go through the view to do it.
TL;DR
When I create a new Window, the content of the previous window disappears. The problem is almost certainly with the control however I am stuck as to how to fix the problem.
This is quite a large problem to look at so I'd be very grateful for anyone who can look into it. Again, the source code is here, please feel free to suggest alternatives or pick out anything that I may have overlooked.
Had some time to spare, so:
The problem is the setter for the CollectionView property in the style for AnagramPagedItemsControl in generic.xaml.
This does not instantiate a new ListBox every time the style is applied; it will just create the one ListBox, the first time the style is created, and use that value over, and over again. So in effect, every instance of MainWindow shares the same ListBox.
You can see this by setting the Tag property of PART_CollectionView to (for instance) "1" in SetupBindings(ItemsControl PART_CollectionView). When you open a new window, you'll see that PART_CollectionView.Tag contains the same value you previously assigned.
I have created two separate UserControls and (depending on which RadioButton is selected) I would like one or the other to be displayed.
Right now, I simply dragged one instance of each UserControl onto the form and placed one on top of another (setting one .Visible = false;).
This is OK, but I was wondering if there was a better or more appropriate way to do this?
That's perfectly reasonable.
If you're concerned about keeping around resources you're no longer using, you can add a Panel and add or remove the control from there.
E.g.
try
{
panel.SuspendLayout();
panel.Controls.Remove(userControl1);
panel.Controls.Add(userControl2);
}
finally
{
panel.ResumeLayout();
}
If the user controls do things like connect to data sources, you might want to actually dispose and recreate them. Really depends on how complex the controls are. If they're just capturing a few properties, your current solution is fine.
I'm creating something like a wizard, and I'm using several User Control, but the roblem is I need to get the parent from that element to replace for the next user control.
How can I do that?
Lets think you have 5 UserControls. While creating wizard you need to add new UserControl inside a grid and remove the previous UserControl from the same parent grid.
The following function will automatically remove the older UserControl and add the new UserControl.. But for the first UserControl you can directly add it to its parent using MyParentPanel.Children.Add(myFirstUserControl);
private void AddNewUserControlAndAutoRemoveOldUserControl(UserControl control)
{
if (control != null)
{
Panel parent = control.Parent as Panel;
if (parent != null)
{
// Removing old UserControl if present
if(parent.Children.Count > 0)
parent.Children.RemoveAt(0);
parent.Children.Insert(0, control);
}
}
}
}
Hope this helps you!
Well, there are various ways you can accomplish a wizard, but the simplest would be to manage the UserControls from your main form. Just add an area to the main form that will be the parent of each user control and then add/remove a user control from the container when necessary.
The most elegant way to do this (I'd say the best) is using a Selector or a ListBox.
Your wizard will have several pages, each one exposes one or more informations, thus controls bound to some data. In other words, you should consider having a "model" containing the data, where the pages will bind to.
Now, consider having a distinct model for each page, and a list of these models feeding the ListBox. This ListBox should have defined its ItemTemplateSelector, that allows to choose a certain DataTemplate upon the item data (i.e. the model).
The hardest part of this technique is to create/define the Control template for the ListBox, that should be shaped for displaying only the selected item (SelectedItem). In such a way, you only have to change the current selection, and the wizard page will show automatically.
Although this technique appears an overkill, it is dramatically convenient respect to the "classic" approach. Your code is much more clean, easier to debug, reutilizable, and offers high separation between modules. All that will give much more reliability, and fast development.
I have not been able to find a clean solution to the following problem even though there are a few related questions already on SO.
If I have a data template that is being used multiple times, for example, a TreeViewItem.HeaderTemplate, how can I change something the template for only some of the TreeViewItems.
For example, let's say my TVI HeaderTemplate has a textblock and depending on the string, I want to make the fontweight bold.
I want to do something like this:
((TextBlock)myTreeView.Items.ElementAt(0).FindName("myTextBlock")).FontWeight = FontWeights.Bold;
Does anyone have a solution for this? --> Thanks Evan
Edit: Is there a way to write a generic function to get a control based on it's name even if it's in a data template?
LayoutRoot.FindName("myTextBlock"); would work if myTextBlock was not in a datatemplate. How can I write a findElementInDataTemplate(string elementName, string parentName) function?
The reason Evan's answer is not what I'm looking for is because I am developing a control. I want the application developer who uses my control to be able to change any element in the control. If I use Evan's solution, that would require the application developer to have access to all the templates in the control. Possible, but not ideal. Thanks!
One way I have accomplished this is to store all the needed items in a class-level collection variable by using the Loaded event of the control. Take this DataTemplate for example.
<DataTemplate>
...
<TextBlock Loaded="TemplateTextBlock_Loaded" />
</DataTemplate>
Then you use the Loaded event to load up some sort of collection for later use.
private List<TextBlock> templateTextBlocks = new List<TextBlock>();
private void TemplateTextBlock_Loaded(object sender, RoutedEventArgs e)
{
TextBlock tb = sender as TextBlock;
if (!this.templateTextBlocks.Contains(tb)) this.templateTextBlocks.Add(tb);
}
Of course, if you're going to be loading and unloading the control, this may not work well for you.
If you're using data binding, have you tried using a binding converter? In this case you would do something like...
FontWeight={Binding Path=TextProperty, Converter={StaticResource BoldConverter}}
And the converter would be along the lines of...
string myTestString = (string)value;
if (myTestString.Contains("Bob"))
return FontWeights.Bold;
return FontWeights.Normal;
Which makes it less painful to try and root through the elements to locate a particular one.
My first reaction to such a requirement would be: are you really sure you want to be doing that? I would normally urge developers to look at the existing control patterns being used. In this case what you seem a Templated control would seem warranted.
Of course this doesn't provide the flexibility you are after. What you seem to be after is the "holy grail" of customisable controls, the desire to tweak any minor detail about the control without having to duplicate the entire template of the control. OF course this isn't really possible declaratively, if it were I'd dread the syntax and semantic rules that would govern it.
Having said that there are always exceptions. So I'll present a possible option despite feeling that you shouldn't be doing this.
This old answer provides a Descendents extension method allow you to enumerate controls across the object tree. Given an instance of a TreeViewItem you should be able to find the TextBlock you are after with:-
TextBlock tb = treeViewItem.Descendents()
.OfType<TextBlock>()
.Where(t => t.Name == "myTextBlock")
.FirstOrDefault();
what version of silverlight is this?
And what year of " Aug 10 at 18:55" is this post from?
in the current version of SL4 it does not seem to be there..
can also try this
TextBlock txtBlk = grd.FindName("txtBlkName") as TextBlock;
where grd = your root element (Parent of the element you are looking for)
Before answering, it is not as easy question as you might have thought about when you read the title.
I have an ItemsControl which is binded to an ObservableCollection of T and data being described as a DataTemplate. So far it is a classic case.
When I add a new element I need to know the exact coordinate and positions inside the window of the element being rendered.
I realize there is a passage of time needed for the Collection to raise the event, and WPF to use all his layouting mechanism to actually position the element.
I wish to be notified when it is done and grab those locations.
I am using those lines of code
UIElement item = list.ItemContainerGenerator.ContainerFromItem(foo) as UIElement;
Point point = TranslatePoint(new Point(0.0, 0.0), Window.GetWindow(item));
The problem is now when I hit those lines it is always premature, If I "wait" for a second and let wPF finish, I do get the right location.
I am trying to find better solutions than "waiting" for the UI-Thread.
Maybe you can help out :)
Thanks!
Ariel
Probably one of UIElement's events will tell you when the layout data is available. LayoutUpdate looks promising.