WPF Run Animation in a separate Thread - wpf

I have an application where I have to bind a Data Grid witch is on an User control in a Tab Item.
This process take a while so I create another Tab Item with a Loading animation in a separate thread and when I load data in data grid I select the tab item with the animation until the data is loaded.
The problem is that the animation is starting, but when the data grid binding starts, the animation is freezing.
this.Dispatcher.Invoke(DispatcherPriority.Normal,
new Action(
delegate()
{
tiLoading.Content = new Controls.Loading.LoadingAnimation();
tiLoading.IsSelected = true;
}
));
//And now I populate the content of the Tab Item with the User Control that contains data grid
connType = choseConnType(); // Here is a dialog witch ask the user somethig and return a value
tiGeneral.Content = new Windows.General(true, connType);
After the dialog when starts the binding the LoadingAnimation is freezing.

It's understandable. If you have long-running process you should be using BackgroundWorker to run it.
public void ItsGonnaBeLong()
{
this.IsBusy = true;
var worker = new BackgroundWorker();
worker.DoWork += this.MyDoWorkMethod;
worker.RunWorkerCompleted += this.MyDoWorkCompleted;
worker.RunWorkerAsync();
}
private void MyDoWorkCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// executed after work on background thread is completed
this.IsBusy = false;
}
private void MyDoWorkMethod(object sender, DoWorkEventArgs e)
{
// Do something.
}
And is it pretty to have your animation on different TabItem? Have you considered using BusyIndicator from Extended WPF Toolkit? You can simply wrap your Control with BusyIndicator and use Its IsBusy property to run it.
<xctk:BusyIndicator IsBusy="True" >
<my:MyUserControl />
</xctk:BusyIndicator>

Related

How to add tab items to existing tab control from user Control in wpf

I have MainWindow and 2 user controls. In Main Window there is Tab Control which loads User Control if you click on button search in MainWindow. I could add tab items in main Window by this code.
private void search(object sender, RoutedEventArgs e)
{
tc.Visibility = Visibility.Visible; // It is hidden by default
TabItem tab = new TabItem();
tab.Header = "Поиск";
UCSearch c = new UCSearch(); // User Control 1
tab.Content = c;
tc.Items.Add(tab);
}
When User Control 1 is loaded in Tab item. There is Tetxbox and Button in User Control 1.I want to load User Control 2 when is clicking to Button. But I can not get access to Tab Control which is in Main Window from User Control 1. Please Give me direction. Where to dig?
You could use an Extension method to search the VisualTree for a Parent of type TabControl.
e.g.
Extension method:
public static class VisualTreeExtensions
{
public static T FindParent<T>(this DependencyObject child)
where T : DependencyObject
{
//get parent item
DependencyObject parentObject = VisualTreeHelper.GetParent(child);
//we've reached the end of the tree
if (parentObject == null) return null;
//check if the parent matches the type we're looking for
var parent = parentObject as T;
if (parent != null)
{
return parent;
}
else
{
return FindParent<T>(parentObject);
}
}
In your Button Handler:
private void Button_Click(object sender, RoutedEventArgs e)
{
var tabControl = (sender as Button).FindParent<TabControl>();
tabControl.Items.Add(new TabItem() { Header = "New"});
}
The better and more flexible (but also more complicated) solution would be to notify the participants (here: your Button fires some kind of message that it was clicked, others (your TabControl) listen and react on it (create a new Tab).
This can for example be done with a Mediator pattern or an EventAggregator.

How to fade an image in on load silverlight?

Can anyone point me to a resource on how to "fade in" an image on load in silverlight? Basically I have a listbox of items that are returned from a web service and some times the images take a little longer to load, so I wanted to fade them in as they download. I read that I might need a storyboard for this effect. Is this the best route or are there alternatives?
I'm not aware of any alternatives. The StoryBoard is the best route. You could just animate the Opacity of the image from 0 to 100.
Put the Storyboard in the resources for the UserControl () or in the App.xaml.
Then in your OnOpened event (as you mentioned in the comment):
protected void OnOpened(object sender, EventArgs e)
{
// params might be incorrect
this.fadeInStoryBoard.Stop();
// your image controls will need x:names set
this.fadeInAnimation.SetValue(Storyboard.TargetNameProperty, ((Image)sender).Name);
this.fadeInStoryBoard.Start();
}
The example storyboard is from:
http://www.codeproject.com/KB/silverlight/AgDynAnimations.aspx
and starting the storyboard: http://blogs.msdn.com/b/silverlight_sdk/archive/2008/03/26/target-multiple-objects-properties-with-one-animation-silverlight.aspx
Here's a utility function to perform the task
private void FadeIn(UIElement uilelement)
{
uilelement.Opacity = 0.0;
uilelement.Visibility = Visibility = Visibility.Visible;
var timeline = new DoubleAnimation() { To = 1.0, Duration =TimeSpan.FromSeconds(3.0) };
Storyboard.SetTarget(timeline, uilelement);
Storyboard.SetTargetProperty(timeline, new PropertyPath(UIElement.OpacityProperty));
var sb = new Storyboard();
sb.Children.Add(timeline);
sb.Begin();
}
function void image1_Opened(object sender, EventArgs e)
{
FadeIn(sender as Image);
}
You should set the Image to Collapsed, or set its initial Opacity to 0.
<Image Source="{Binding ImagePath}"
Name="image1"
Visibility="Collapsed"
ImageOpened="image1_ImageOpened" />

Silverlight Without XAML - Images Will Not Render

I have a Silverlight application in which I'm not using XAML. I have a basic application with the following code in Application_Startup:
private void Application_Startup(object sender, StartupEventArgs e)
{
Grid g = new Grid();
g.Children.Add(new Image { Source = new System.Windows.Media.Imaging.BitmapImage(new Uri("http://sstatic.net/so/img/sprites.png", UriKind.Absolute)) });
this.RootVisual = g;
}
This code will not render the specified image. If however, the App.Xaml file is modified to define the RootVisual in the Xaml the following works:
xaml:
<Application.RootVisual>
<Grid>
</Grid>
</Application.RootVisual>
code:
private void Application_Startup(object sender, StartupEventArgs e)
{
((Grid)this.RootVisual).Children.Add(new Image { Source = new System.Windows.Media.Imaging.BitmapImage(new Uri("http://sstatic.net/so/img/sprites.png", UriKind.Absolute)) });
}
I don't see why one would work and the other not. I have the same behavior using a UserControl as well (using Content instead of Childern of course).
From what I understand, there should be not XAML requirement. Is there something I'm missing?
The difference is in the first case you are setting the RootVisual to be a Grid, but in the second your grid is a child element.
On the MSDN page for the RootVisual property it shows the following example:
this.RootVisual = new Page();
so if you create a Page and then add your Grid to that page it should work.
Page page = new Page();
page.Content = g;
this.RootVisual = page;

Open MediaElement in FullScreen

There is a MediaElement in my silverLight4 Application that plays videos.
There are also other controls in the form (listbox, buttons etc...).
When vieweing a Video, i want the option to switch to Fullscreen, but only the video and not all the form (something like youtube), is there a way to switch just the 'MediaElement' control to fullscreen?
Make your MediaElement the RootVisual of the app. Since you can't change the RootVisual once it's assigned you need to do something like so
private MainPage _mainPage = new MainPage();
private MediaElement _media = new MediaElement();
private void Application_Startup(object sender, StartupEventArgs e)
{
Grid grid = new Grid();
grid.Children.Add(_mainPage);
this.RootVisual = grid;
}
public void FullscreenVideo()
{
(this.RootVisual as Grid).Children.Clear();
(this.RootVisual as Grid).Children.Add(_media);
Application.Current.Host.Content.IsFullScreen = true;
}
If you call FullscreenVideo it should load your MediaElement into a fullscreen window

How can I move a WPF Popup when its anchor element moves?

I have a Popup defined like this:
<Popup
Name="myPopup"
StaysOpen="True"
Placement="Bottom"
PlacementRectangle="0,20,0,20"
PlacementTarget="{Binding ElementName=myPopupAnchor}">
<TextBlock ... />
</Popup>
I have added event handlers to the myPopupAnchor element for the events MouseEnter and MouseLeave. The two event handlers toggle the popup's visibility.
My problem is the position of myPopupAnchor is only read when the popup is first shown, or hidden and then shown again. If the anchor moves, the popup does not.
I'm looking for ways to work around this, I want a moving Popup. Can I notify WPF that the PlacementTarget binding has changed and should be read again? Can I manually set the popup's position?
Currently, I have a very crude workaround that involves closing and then opening the popup again, which causes some repainting issues.
I looked at a couple options and samples out there. The thing that seems to work best for me is to "bump" one of the properties that causes the Popup to reposition itself on its own. The property that I used is HorizontalOffset.
I set it to (itself + 1) and then set it back the original value. I do this in an event handler that runs when the window is repositioned.
// Reference to the PlacementTarget.
DependencyObject myPopupPlacementTarget;
// Reference to the popup.
Popup myPopup;
Window w = Window.GetWindow(myPopupPlacementTarget);
if (null != w)
{
w.LocationChanged += delegate(object sender, EventArgs args)
{
var offset = myPopup.HorizontalOffset;
myPopup.HorizontalOffset = offset + 1;
myPopup.HorizontalOffset = offset;
};
}
When the window is moved, the popup will reposition. The subtle change in the HorizontalOffset is not noticed because the window and popup are already moving anyway.
I'm still evaluating whether a popup control is the best option in cases where the control stays open during other interaction. I'm thinking that Ray Burns suggestion to put this stuff in an Adorner layer seems like a good approach for some scenarios.
Just to add on to NathanAW's great solution above, I thought I'd point out some context, such as where to place the C# code in this case. I'm still pretty new to WPF so I struggled at first to figure out where to put NathanAW's code. When I tried putting that code in the constructor for the UserControl that hosted my Popup, Window.GetWindow() always returned Null (so the "bump" code never executed). So I thought that other newbies might benefit from seeing things in context.
Before showing the C# in context, here's some example XAML context to show some relevant elements and their names:
<UserControl x:Class="MyNamespace.View1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
<TextBlock x:Name="popupTarget" />
<Popup x:Name="myPopup"
Placement="Bottom"
PlacementTarget="{Binding ElementName=popupTarget}" >
(popup content here)
</Popup>
</UserControl>
Then in the code-behind, to avoid having Window.GetWindow() return Null, wire up a handler to the Loaded event to house NathanAW's code (see Peter Walke's comment on a similar stackoverflow discussion for example). Here's exactly how it all looked in my UserControl code-behind:
public partial class View1 : UserControl
{
// Constructor
public View1()
{
InitializeComponent();
// Window.GetWindow() will return Null if you try to call it here!
// Wire up the Loaded handler instead
this.Loaded += new RoutedEventHandler(View1_Loaded);
}
/// Provides a way to "dock" the Popup control to the Window
/// so that the popup "sticks" to the window while the window is dragged around.
void View1_Loaded(object sender, RoutedEventArgs e)
{
Window w = Window.GetWindow(popupTarget);
// w should not be Null now!
if (null != w)
{
w.LocationChanged += delegate(object sender2, EventArgs args)
{
var offset = myPopup.HorizontalOffset;
// "bump" the offset to cause the popup to reposition itself
// on its own
myPopup.HorizontalOffset = offset + 1;
myPopup.HorizontalOffset = offset;
};
// Also handle the window being resized (so the popup's position stays
// relative to its target element if the target element moves upon
// window resize)
w.SizeChanged += delegate(object sender3, SizeChangedEventArgs e2)
{
var offset = myPopup.HorizontalOffset;
myPopup.HorizontalOffset = offset + 1;
myPopup.HorizontalOffset = offset;
};
}
}
}
private void ppValues_Opened(object sender, EventArgs e)
{
Window win = Window.GetWindow(YourControl);
win.LocationChanged += new EventHandler(win_LocationChanged);
}
void win_LocationChanged(object sender, EventArgs e)
{
if (YourPopup.IsOpen)
{
var mi = typeof(Popup).GetMethod("UpdatePosition", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
mi.Invoke(YourPopup, null);
}
}
If you want to move the popup, there is a simple trick : change its position,then set :
IsOpen = false;
IsOpen = true;
To add to Jason Frank's answer, the Window.GetWindow() approach wouldn't work if the WPF UserControl is ultimately hosted in an WinForms ElementHost. What I needed to find was the ScrollViewer that my UserControl was placed in, as that was the element showing the scrollbars.
This generic recursive method (modified off another answer) will help find the parent of a particular type in the logical tree (it's possible to use the visual tree too), and return it if found.
public static T FindLogicalParentOf<T>(DependencyObject child) where T: FrameworkElement
{
DependencyObject parent = LogicalTreeHelper.GetParent(child);
//Top of the tree
if (parent == null) return null;
T parentWindow = parent as T;
if (parentWindow != null)
{
return parentWindow;
}
//Climb a step up
return FindLogicalParentOf<T>(parent);
}
Call this helper method instead of Window.GetWindow() and continue with Jason's answer of subscribing to the right events. In the case of ScrollViewer, it's the ScrollChanged event instead.
I modified the Code from Jason, because the Popup is already in Foreground if the Window is not Activated. Is there any Option in the Popup class or i is my solution ok?
private void FullLoaded(object sender, RoutedEventArgs e) {
Window CurrentWindow = Window.GetWindow(this.Popup);
if (CurrentWindow != null) {
CurrentWindow.LocationChanged += (object innerSender, EventArgs innerArgs) => {
this.RedrawPopup();
};
CurrentWindow.SizeChanged += (object innerSender, SizeChangedEventArgs innerArgs) => {
this.RedrawPopup();
};
CurrentWindow.Activated += (object innerSender, EventArgs innerArgs) => {
if (this.m_handleDeActivatedEvents && this.m_ShowOnActivated) {
this.Popup.IsOpen = true;
this.m_ShowOnActivated = false;
}
};
CurrentWindow.Deactivated += (object innerSender, EventArgs innerArgs) => {
if (this.m_handleDeActivatedEvents && this.Popup.IsOpen) {
this.Popup.IsOpen = false;
this.m_ShowOnActivated = true;
}
};
}
}
private void RedrawPopup() {
double Offset = this.Popup.HorizontalOffset;
this.Popup.HorizontalOffset = Offset + 1;
this.Popup.HorizontalOffset = Offset;
}
I encapsulated the logic provided by Jason Frank in a class and inherit from the PopUp class.
class MyPopup : Popup
{
private Window _root;
public MyPopup()
{
Loaded += OnLoaded;
Unloaded += OnUnloaded;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
_root = Window.GetWindow(this);
_root.LocationChanged += OnRootLocationChanged;
}
private void OnRootLocationChanged(object sender, EventArgs e)
{
var offset = this.HorizontalOffset;
this.HorizontalOffset = offset + 1;
this.HorizontalOffset = offset;
}
private void OnUnloaded(object sender, RoutedEventArgs e)
{
_root.LocationChanged -= OnRootLocationChanged;
Loaded -= OnLoaded;
Unloaded -= OnUnloaded;
}
}
You can not do this. When Popup is displayed on the screen, it does not reposition itself if its parent is repositioned. Thats the behavior of Popup control.
check this: http://msdn.microsoft.com/en-us/library/system.windows.controls.primitives.popup.aspx
you can use a Window(with WindowStyle=None) instead of Popup that may solve your problem.
Download the Popup Popup Position Sample at:
http://msdn.microsoft.com/en-us/library/ms771558(v=VS.90).aspx
The code sample uses the class CustomPopupPlacement with a Rect object, and binds to horizontal and vertical offsets to move the Popup.
<Popup Name="popup1" Placement="Bottom" AllowsTransparency="True"
IsOpen="{Binding ElementName=popupOpen, Path=IsChecked}"
HorizontalOffset="{Binding ElementName=HOffset, Path=Value, Mode=TwoWay}"
VerticalOffset="{Binding ElementName=VOffset, Path=Value, Mode=TwoWay}"

Resources