How to load a UserControl in WPF with reflection? - wpf

private void Window_Loaded(object sender, RoutedEventArgs e)
{
var assm = Assembly.LoadFrom("wpflib.dll");
foreach (var t in assm.GetTypes())
{
var i = t.GetInterface("test.ILib");
if (i != null)
{
var tmp = Activator.CreateInstance(typeof(UserControl)) as UserControl;
this.stackPanel1.Children.Add(tmp);
}
}
}
tmp(UserControl1 in wpflib.dll) only contains a label and a textbox.
Windows1 (test.exe) reference ILib.dll, and only contains a stackPanel1.
But, why there is nothong in Windows1(stackPanel1)?

You are not instantiating the type from the DLL at all. Instead of:
var tmp = Activator.CreateInstance(typeof(UserControl)) as UserControl;
write:
var tmp = Activator.CreateInstance(t) as UserControl;
Furthermore, I would recommend that you actually write
var tmp = (UserControl) Activator.CreateInstance(t);
instead. Otherwise, if you have a bug, you will get a null-reference exception later on, which is not very informative and hard to debug. This way you get a more meaningful type-cast exception in the right place where the bug actually happens.

Related

WPF No NameScope found to register the Name

I am trying to create a "debug" TextBlock, in the sense it is just like a MessageBox, but doesn't interrupt like it. Rather, it is like a statusbar, which gives output information silently. Here is my current code
private void Debug(string data)
{
TextBlock tb = componentContainer.FindName("Debugbox") as TextBlock;
if (tb == null)
{
MessageBox.Show("yo");
tb = new TextBlock() {Foreground = Brushes.Orange };
if (NameScope.GetNameScope(tb) == null)
NameScope.SetNameScope(tb, new NameScope());
componentContainer.RegisterName("Debugbox", tb);
componentContainer.Children.Add(tb);
}
tb.Text = data;
}
However, it is giving me "No NameScope found to register the Name". I tried replacing
componentContainer.RegisterName("Debugbox", tb);
with
Namescope.GetNameScope(tb).RegisterName("Debugbox", tb);
and it works well. But if this function is used multiple times, the TextBlock is simply overwritten which makes the required output hard to see.
NOTE : componentContainer is a Canvas
Does anyone know what I am doing wrong? Or is there any better way to do something similar?
Why not simply keep the TextBlock instance in a private field:
private TextBlock debugBox;
private void Debug(string data)
{
if (debugBox == null)
{
debugBox = new TextBlock { Foreground = Brushes.Orange };
componentContainer.Children.Add(debugBox);
}
debugBox.Text = data;
}

How to work with webClient.OpenReadCompleted event handler?

I'm using the following code and it works nice
private void somethingButton_Click(object sender, System.Windows.RoutedEventArgs e)
{
WebClient webClient = new WebClient();
webClient.OpenReadCompleted += webClient_OpenReadCompleted;
webClient.OpenReadAsync(new Uri(myUri));
}
void webClient_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
if(e.Error != null)
{
messageTextBlock.Text = e.Error.Message;
return;
}
using (Stream s=e.Result)
{
XDocument document = XDocument.Load(s);
var q1 = from c in document.Descendants("result")
select new IndeedResult
{
Title =((string)c.Element("title")).Trim(),
ResultUri = ((string)c.Element("url")).Trim(),
Date = ((string)c.Element("date")).Trim(),
};
myDataGrid.ItemsSource = q1;
}
But I want to add another WebClient, webClient2 that will do exactly the same but has different uri and different structure so I will have webClient2_OpenReadCompleted...
The problem is that finally I need to merge (or do some logic before merge) var q1 from webClient_OpenReadCompleted and var q2 from webClient2_OpenReadCompleted and then
var mergedQs = q1.Union(q2).ToList();
myDataGrid.ItemsSource = mergedQs
Is there a simple way how to do it? I don't know how to do it using those event handlers.
Good question! Did it like this
void webClient_OpenReadCompleted( object sender, OpenReadCompletedEventArgs e )
{
Stream stream = (Stream)e.Result;
BinaryReader reader = new BinaryReader( stream );
byte[] buffer = reader.ReadBytes( (int)stream.Length );
Uri uri = (Uri)e.UserState;
streams.Add( uri.AbsoluteUri, new MemoryStream( buffer ) );
}
Note the usage of UserState in order to provide a unqiue key into my streams Dictionary. Works perfectly :-) This way you can work with as many files / images / binary data as you like!
See this to see just how powerful this can be....http://www.alansimes.com/warp3dsilverlighttestpage.html

Why would 'this.ContentTemplate.FindName' throw an InvalidOperationException on its own template?

Ok... this has me stumped. I've overridden OnContentTemplateChanged in my UserControl subclass. I'm checking that the value passed in for newContentTemplate does in fact equal this.ContentTemplate (it does) yet when I call this...
var textBox = this.ContentTemplate.FindName("EditTextBox", this);
...it throws the following exception...
"This operation is valid only on elements that have this template applied."
Per a commenter in another related question, he said you're supposed to pass in the content presenter for the control, not the control itself, so I then tried this...
var cp = FindVisualChild<ContentPresenter>(this);
var textBox = this.ContentTemplate.FindName("EditTextBox", cp);
...where FindVisualChild is just a helper function used in MSDN's example (see below) to find the associated content presenter. While cp is found, it too throws the same error. I'm stumped!!
Here's the helper function for reference...
private TChildItem FindVisualChild<TChildItem>(DependencyObject obj)
where TChildItem : DependencyObject {
for(int i = 0 ; i < VisualTreeHelper.GetChildrenCount(obj) ; i++) {
var child = VisualTreeHelper.GetChild(obj, i);
if(child is TChildItem typedChild) {
return typedChild;
}
else {
var childOfChild = FindVisualChild<TChildItem>(child);
if(childOfChild != null)
return childOfChild;
}
}
return null;
}
Explicitly applying the template before calling the FindName method will prevent this error.
this.ApplyTemplate();
As John pointed out, the OnContentTemplateChanged is being fired before it is actually applied to the underlying ContentPresenter. So you'd need to delay your call to FindName until it is applied. Something like:
protected override void OnContentTemplateChanged(DataTemplate oldContentTemplate, DataTemplate newContentTemplate) {
base.OnContentTemplateChanged(oldContentTemplate, newContentTemplate);
this.Dispatcher.BeginInvoke((Action)(() => {
var cp = FindVisualChild<ContentPresenter>(this);
var textBox = this.ContentTemplate.FindName("EditTextBox", cp) as TextBox;
textBox.Text = "Found in OnContentTemplateChanged";
}), DispatcherPriority.DataBind);
}
Alternatively, you may be able to attach a handler to the LayoutUpdated event of the UserControl, but this may fire more often than you want. This would also handle the cases of implicit DataTemplates though.
Something like this:
public UserControl1() {
InitializeComponent();
this.LayoutUpdated += new EventHandler(UserControl1_LayoutUpdated);
}
void UserControl1_LayoutUpdated(object sender, EventArgs e) {
var cp = FindVisualChild<ContentPresenter>(this);
var textBox = this.ContentTemplate.FindName("EditTextBox", cp) as TextBox;
textBox.Text = "Found in UserControl1_LayoutUpdated";
}
The ContentTemplate isn't applied to the ContentPresenter until after that event. While the ContentTemplate property is set on the control at that point, it hasn't been pushed down to bindings internal to the ControlTemplate, like the ContentPresenter's ContentTemplate.
What are you ultimately trying to do with the ContentTemplate? There might be a better overall approach to reach your end goal.

wp7, silverlight, Why event fires twice?

In this case when user click some UIElement I want this element yo be surrounded by dashed border. If there is some other object surrounded then this code removes it first.
Everything is fine but "private void activateElem" fires twice and I have no idea why. Maybe somebody can help, I really have no more ideas.
Anybody knows some ideas to debug issues like this? Maybe there is some way to print out all events queue. There is silverlight spy but I think it doesn't work for windows phone 7.
My code:
public class ManipulationEngine
{
private Canvas sheet;
private static FrameworkElement current_active_element;
public ManipulationEngine(Canvas sheet)
{
this.sheet = sheet;
foreach (FrameworkElement elem in sheet.Children)
{
elem.MouseLeftButtonUp += new MouseButtonEventHandler(activateElem);
}
}
private void activateElem(object sender, MouseButtonEventArgs e)
{
FrameworkElement elem = sender as FrameworkElement;
if (current_active_element != null)
{
desactivateElem();
}
Grid grid = new Grid();
Rectangle recentagle = new Rectangle();
grid.SetValue(Canvas.TopProperty, (double)elem.GetValue(Canvas.TopProperty) - 10);
grid.SetValue(Canvas.LeftProperty, (double)elem.GetValue(Canvas.LeftProperty) - 10);
DoubleCollection stroke = new DoubleCollection();
stroke.Add(4);
stroke.Add(2);
recentagle.StrokeDashArray = stroke;
grid.Children.Add(recentagle);
sheet.Children.Remove(elem);
elem.Margin = new Thickness(10);
grid.Children.Add(elem);
sheet.Children.Add(grid);
current_active_element = elem;
}
private void desactivateElem()
{
if (current_active_element != null)
{
Grid grid = VisualTreeHelper.GetParent(current_active_element) as Grid;
grid.Children.Remove(current_active_element);
sheet.Children.Remove(grid);
current_active_element.SetValue(Canvas.TopProperty, (double)grid.GetValue(Canvas.TopProperty) + 10);
current_active_element.SetValue(Canvas.LeftProperty, (double)grid.GetValue(Canvas.LeftProperty) + 10);
current_active_element.Margin = new Thickness(0);
sheet.Children.Add(current_active_element);
current_active_element = null;
}
}
I'd really advise looking into the Parts and States model. You may be able to do this with a button, or perhaps a radio button.
Usually if you're coding changes to the visual tree, you're not doing it right.
Karen Corby dealt with this very clearly at MIX08, take a look!
http://archive.visitmix.com/blogs/2008Sessions/T20/
Luke

C# EventHandler Beautiful Code (How To?)

I admit, it is kind of tiny, but I am looking for better ways to do the following code blocks. They should be self explaining...
private void listBoxItem_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
var listBoxItem = sender as ListBoxItem;
if (listBoxItem != null)
{
var clickObject = listBoxItem.DataContext as ClickObject;
if (clickObject != null)
{
clickObject.SingleClick();
}
}
}
Another ugly one:
private void listBox_SelectionChangedA(object sender, SelectionChangedEventArgs e)
{
var lB = sender as ListBox;
if (lB != null)
StatusBoxA.Text = "Elements selected" + lB.SelectedItems.Count;
}
Yeah, I know, its not near-death-urgent. But I do NOT like the (if != null). Any magic ideas to shorten it even more :-)
Btw, I found some nice info about a similar topic: Loops on Null Items
Nice to read...
I love good, clean code but in most cases, clean & elegant doesn't mean short and smart. Code brevity is good for competitions. Changing an "if not null" statement to a foreach might seem way cooler but it's harder for everyone else working in the project to understand what you are trying to accomplish. Believe me, even you won't remember it a few months later :P. Your code is just fine as it is!
private void listBoxItem_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
var listBoxItem = sender as ListBoxItem;
if (listBoxItem == null) return;
var clickObject = listBoxItem.DataContext as ClickObject;
if (clickObject == null) return;
clickObject.SingleClick();
}
One-liner:
private void listBox_SelectionChangedA(object sender, SelectionChangedEventArgs e)
{
As<ListBox>(sender, (lB) => StatusBoxA.Text = "Elements selected" + lB.SelectedItems.Count);
}
or, nested:
private void listBoxItem_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
As<ListBoxItem>(sender, (listBoxItem) => {
As<ClickObject>(listBoxItem.DataContext,
(clickObject) => clickObject.SingleClick());
};
}
using this static generic method (T is destination type, input is object to cast, code is a delegate (or lambda expression) to execute on success:
static void As<T>(object input, Action<T> code) where T : class
{
T foo = input as T;
if (foo != null)
code(foo);
}
Since you're using known events from the .NET framework (as opposed to a third party) and from the code it looks like you're only using those methods for specific classes (i.e. ListBoxItems and ListBoxes), there are a few things you know to be true:
sender will never be null
sender will always be a ListBoxItem, or ListBox, respectively
So why use the as operator? Just cast!
Then the first snippet becomes
private void listBoxItem_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
var listBoxItem = (ListBoxItem)sender;
var clickObject = (ClickObject)listBoxItem.DataContext;
clickObject.SingleClick();
}
Note this isn't true in the general case (you wouldn't do this if you were handling all PreviewMouseDown events in that one handler for all Control types), but for event handling code like this, especially in UI code, you can be as certain as you can be of anything, that sender will not be null and sender will be of the type you expect.
Maybe I am just being pedantic but why do you need to cast the sender if you are using the event within its host containers code.
Regardless of who made the change to a list, couldn't you just give your listbox a name and use that.
<ListBox x:Name="listbox1" />
private void listBox_SelectionChangedA(object sender, SelectionChangedEventArgs e)
{
StatusBoxA.Text = "Elements selected" + listbox1.SelectedItems.Count;
}
Or you could even achieve some of this using binding with no code behind.
This is supposed to be the same as the first one, reformatted a little:
private void listBoxItem_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
ClickObject clickObject;
if (
((sender as ListBoxItem) != null) &&
((clickObject = ((ListBoxItem)sender).DataContext as ClickObject) != null)
)
{
clickObject.SingleClick();
}
}
You can add extensions methods to Form elements, which can then trigger the events:
public static void OnSelectionChanged(this ListBox b, Action<ListBox> a)
{
b.SelectedIndexChanged += (s,e) =>
{
if (s is ListBox)
a(s as ListBox);
};
}
Using the same idea as Utaal's solution, but as an extension method...
public static void As<TSource>(this object item, Action<TSource> action) where TSource : class
{
var cast = item as TSource;
if (cast != null)
action(cast);
}
private void listBoxItem_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
sender.As<ListBoxItem>(listBoxItem =>
listBoxItem.DataContext.As<ClickObject>(clickObject =>
clickObject.SingleClick()));
}

Resources