Sharing same vector control between different places - silverlight

I'm trying to implement the following: I have an Items Manager, that has an Item class inside. Item class can store two possible visual representations of it - BitmapImage(bitmap) and UserControl(vector).
Then later, in the game, I need to share the same image or vector control between all possible places it takes place. For example, consider 10 trees on the map, and all point to the same vector control. Or in some cases this can be bitmap image source.
So, the problem is that BitmapImage source can be easily shared in the application by multiple UIElements. However, when I try to share vector control, it fails, and says Child Element is already a Child element of another control. I want to know how to organize this in the best way. For example replace UserControl with other type of control, or storage, however I need to be sure it supports Storyboard animations inside.
The code looks like this:
if (bi.item.BitmapSource != null)
{
Image previewImage = new Image();
previewImage.Source = bi.item.BitmapSource;
itemPane.ItemPreviewCanvas.Children.Add(previewImage);
} else
if (bi.item.VectorSource != null)
{
UserControl previewControl = bi.item.VectorSource;
itemPane.ItemPreviewCanvas.Children.Add(previewControl);
}
Or it is not possible to share same control in different places, then what is the best way to make a copy, or the best way to store vector data.
Thanks in advance

So, I found the problem. It is possible to attach the same UserControl to different controls.
However, when on update I was deleting control, and then filling up it again with a new pointer, that sometimes was the same as before deleting, somehow it was still in memory. And so it was like 2 same user control attached to the same parent.
I added a line of code that was cleaning all children in control, before updating it with new vector UserControl, and now works like a charm.

Related

Switch WPF UI controls dynamically

I need to develop a simple WPF application. In the UI window, There are Labels and Text Blocks towards the left and Buttons towards the right.
Figure 1
Based on a config setting (whether the user is left-handed or right-handed) I need to switch the controls, Buttons towards the left and Labels and Text Blocks towards the right.
Figure 2
Can you please recommend a good way to address this requirement?
Depends what the scope of the app is likely to be.
2 alternatives:
1)
I think it likely as an app grows that there will be more than just buttons.
I would probably build a usercontrol which encapsulates this behaviour for a label and control. The usercontrol uses a static to decide where the textblocks are positioned but would look something like the row edit control in this:
https://gallery.technet.microsoft.com/WPF-Entity-Framework-MVVM-78cdc204
Which is a usercontrol has a contentpresenter in it so you can put any control you like ( such as a button ) "in" it and set a dependency property for the label.
2)
Define 2 contentcontrol templates similar to the one used in this:
https://social.technet.microsoft.com/wiki/contents/articles/28597.aspx
Put them in separate resource dictionaries and give them the same key.
Merge into application.current.resources the appropriate resource dictionary and hence style.
Seeing as this is an app setting, this is presumably a start up thing. People don't just change their "handedness" dynamically. So you could probably use these as staticresource. If they're realistically going to change at run time then I think this would be a bit more involved because you'd need to force re render of a view.
2 Templates are probably the right and stylish solution here as #RajN said.
Also you can define a grid with 2 columns and switch the property 'Grid.Column' of each controls accordingly
Maybe not the best way, but I managed to achieve this using a grid as per your suggestions. Thank you all for your valuable feedback.
I switched the columns and changed the widths accordingly.
if (AppSettings.IsLeft)
{
parentGrid.ColumnDefinitions[0].Width = new GridLength(400, GridUnitType.Pixel);
parentGrid.ColumnDefinitions[1].Width = new GridLength(1, GridUnitType.Star);
Grid.SetColumn(buttonGrid,0);
Grid.SetRow(buttonGrid,0);
Grid.SetColumn(contentGrid,1);
Grid.SetRow(contentGrid,0);
}

Call LoadBaml() once for set of similar user controls

In my project I have to use WPF to place a big set of similar user controls(around 2000) on Canvas object. Basically, it's just a set of rectangles, that can change visibility, can be selected and store data object inside.
I add new controls with help of attached property like this:
public static readonly DependencyProperty VisualStaticBlocksProperty =
DependencyProperty.RegisterAttached("VisualStaticBlocks", typeof(ObservableCollection<VisualBlockViewModel>), typeof(BindableBlocksBehaviour),
new UIPropertyMetadata(null, VisualStaticBlocksPropertyChanged));
private static void VisualStaticBlocksPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
ClickSightView clickSight = source as ClickSightView;
ObservableCollection<VisualBlockViewModel> visualBlocks = e.NewValue as ObservableCollection<VisualBlockViewModel>;
if (clickSight != null && visualBlocks != null)
{
foreach (VisualBlockViewModel visualBlock in visualBlocks)
{
clickSight.StaticBlocksCanvas.Children.Add(new VisualBlockView(visualBlock));
}
}
}
However, it takes a lot of time to build all of them(around 2 seconds). I used a profiler to check that main problem is in LoadBaml() method, which is called in InitializeComponent() method.
As I understand, LoadBaml() is used to parse xaml markup. Is it possible somehow to cache the LoadBaml() result for component and reuse it instead of parse xaml each time I create new control instance?
EDIT:
To represent this set of objects visually I have created user control with Canvas on it, and created attached property VisualStaticBlocks to attachblock view models(type VisualBlockViewModel) to this control and insert visual block instances(type VisualBlockView) directly to Canvas.
EDIT2:
I've solved the problem by giving up using user controls for this purpose at all.
As my controls are quite simple, I used Rectangle() class instead with 3 manually added bindings and 3 manually added events. Of course, there were no InitializeComponent() calls at all.It allowed me to build the set of 2000 rectangles in 200 miliseconds, which is 10 times faster.
Anyway, still will be grateful for information if I can clone similar objects without loading BAML each time.
It sounds like you have an issue with the time it takes to create visual elements. I can see why you think you need to call InitializeComponent, but that is not how WPF works.
As noted here:
The call to InitializeComponent() (which is usually called in the default constructor of at least Window and UserControl) is actually a method call to the partial class of the control (rather than a call up the object hierarchy as I first expected).
Which leads me to suspect you do not understand how (or why) InitializeComponent works; it is impossible to call it once to build multiple elements and externally, no less.
You are using ObservableCollection, which neither works well with large data sets nor complex views. Consider using a thread-safe ObservableCollection and add the data objects on a background thread. This shouldn't be an issue because you're adding data objects (view models) versus visual objects (views); visual objects should be added on the same thread (UI) they are created.
It would help to provide additional information as you have not explained how you represent these objects visually. Is the collection bound to an ItemsControl and does it define a DataTemplate to visually represent each data object?

Directly addressing controls in a UserControl after adding TableLayoutPanel

I recently refactored the code of a user control in my WinForms project, and changed it from a user control with text boxes, combo and buttons that were just placed all over it, to a user control that now contains a TableLayoutPanel, which holds all the controls in a better order.
My problem is that in many places the code address the controls nested in the user control directly via the Controls dictionary - for example: MyUserControl.Controls["NameOfTextBox"].Visible = false;
Now, after I nested the text boxes and buttons in a TableLayoutPanel, I can't do such addressing anymore, and now I should write MyUserControl.Controls[0].Controls["NameOfTextBox"].Visible = false;, because otherwise I get an exception.
My question is whether I should change all my code in every place that addresses the contents of the user control, or can you offer me some workaround to implement on the user control itself, so when I'll try address the controls directly, it will forward it to the contents of the TableLayoutPanel.
Any Ideas?
My problem is that in many places the code address the controls nested in the user control directly...
That's probably the core issue. Try creating properties for you UserControl instead:
public bool NameBoxVisible {
get { return NameOfTextBox.Visible; }
set { NameOfTextBox.Visible = value; }
}
Then you can reference the controls directly in your UserControl but provide a separation of concern to the consumers of your control.

move from form to user control

I have a bunch of Forms that I embed in tabpages(some are embedded two and three layers deep) that I now suspect are giving me trouble. I have been told that User Control's are the better approach.
Now I am wondering how I
canaccomplish this as quick as
possible.
Is it as simple as copy and paste?
Has anyone ever done something like
this?
I have about 40 forms that I embedd that would need to be moved and not a lot of time to do it so any help is greatly appreciated.
EDIT 1
This is how I embed forms:
public static void ShowFormInContainerControl(Control ctl, Form frm)
{
frm.TopLevel = false;
frm.FormBorderStyle = FormBorderStyle.None;
frm.Dock = DockStyle.Fill;
frm.Visible = true;
ctl.Controls.Add(frm);
}
public static void DockControl(this Control control, UserControl userControl)
{
userControl.Dock = DockStyle.Fill;
control.Controls.Clear();
control.Controls.Add(userControl);
}
Not sure if it's the "best", but this is probably the most efficient. Change the classes to inherit from UserControl instead of Form. Then fix the compiler errors if/when you get any (see NOTE 2 below).
NOTE 1: If you're not using version control, start using it before doing something drastic like this. You'll want to be able to go back if something goes too far south.
NOTE 2: If you use any particular events or properties of Form that aren't implemented in UserControl, you'll have to think of a solution. Some properties (Icon for example) you can safely just ignore (= delete the line from the designer file).
NOTE 3: If you use the forms as an actual form somewhere, you'll want to also have a form that uses the newly created UserControl. You're most likely to get in trouble here with naming, so keep a sharp eye.

WPF Bind DataTable to repeated user controls

I'm in the process of teaching myself WPF, and I have run into a small issue that I can't find the answer to.
My test app allows image files to be dropped into a StackPanel. Once an image is dropped, a new user control is added to the stack, and displays some meta-data about the file. All is working correctly, and I can iterate through the child controls to retrieve the values.
What I'd prefer to be able to do is allow the user to persist this data to a file, so they can suspend working on the data. The obvious way for me to do this is to store the data in a DataTable and serialise/deserialise it to xml. However, I don't know how to drive the collection of user controls from a DataTable or DataSet object - in fact, I don't even know if this is the right way to go about it in a WPF app. I am more than willing to admit my ignorance here and take better suggestions if there are any.
Summary of the app logic.
1) File is dropped (from Win explorer) onto a StackPanel
2) File triggers creation of a new user control, which is added to the StackPanel
3) Data is populated in the user control
4) Processing data involves iterating through the control collection.
What I'd like
1) File is dropped (from Win explorer) onto a StackPanel
2) File data is inserted into some persistable object (data table?)
3) updated data table drives the generation of the user control to be added to the displayed collection.
4) save / load functionality persists the data for re-use later.
Thanks in advance
You're on the right track with the second approach, what you need to look at is the ItemsControl - that's a thing which can have items added to it. It's the base for ListBox etc, and you can template it to work as you require. Then there's the DataTemplate which handles which controls are displayed and data binding to those controls when an item is added to the underlying data structure. There are quite a few examples around on the net, try Dr WPF.
In order to make everything work the underlying data structure must support change notification. As everything happens automagically, once the Xaml is setup, you can find yourself in an odd situation. You've added data to a data structure, which in turn has caused controls and data to appear in your ItemsControl. How do you link the data items and their visual controls. The answer is to use some built in static methods ItemFromContainer which links from the graphic to your underlying data item, useful to handle click events, and ContainerFromItem which does the reverse.

Resources