Forcing rerender in WPF 4.0 - wpf

I'm trying to use this code to force my progress view to paint before processing begins:
public static class ProgressBehaviors
{
public static readonly DependencyProperty ForceRepaintProperty =
DependencyProperty.RegisterAttached(
"ForceRepaint",
typeof(bool),
typeof(ProgressBehaviors),
new UIPropertyMetadata(false, OnForceRepaintChanged));
public static bool GetForceRepaint(FrameworkElement treeViewItem)
{
return (bool)treeViewItem.GetValue(ForceRepaintProperty);
}
public static void SetForceRepaint(FrameworkElement treeViewItem, bool value)
{
treeViewItem.SetValue(ForceRepaintProperty, value);
}
static void OnForceRepaintChanged(DependencyObject a_object, DependencyPropertyChangedEventArgs e)
{
Border item = a_object as Border;
if (item == null)
return;
item.IsVisibleChanged += (s, ev) =>
{
item.UpdateLayout();
Window window = Window.GetWindow(item);
if (window != null)
window.Measure(window.RenderSize);
};
}
}
Basic behavior injection. The reason i have to do this is because otherwise the please wait progress message is not shown until after its done doing whatever it has to do. I'm trying UpdateLayout and Measure and neither works. Everybody tells me that I'm doing things wrong in WPF. What, am I supposed to wrap every process I'm doing in a second thread or a Dispatcher invoke? That seems ugly.

Here is a way: http://dedjo.blogspot.com/2007/08/how-to-doevents-in-wpf.html

Related

Synchronization in Silverlight

I have two user controls in Silverlight application containing textboxes(1), how can i synchronize these textboxes when i start writing in one of those textboxes.
Create a dependency property in each control that changes the value of th text box, then bind on control to the value of the other.
example
public static readonly DependencyProperty InnerTextProperty=
DependencyProperty.Register(
"InnerText", typeof(string),
new PropertyMetadata(false, OnTextInput) );
public bool InnerText
{
get { return (bool)GetValue(InnerTextProperty); }
set { SetValue(InnerTextProperty, value); }
}
private static void OnTextInput(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
YourControl c = obj as YourControl
if(c != null)
{
c._innerTextBox.Text = e.Value;
}
}

How to create Windows 8 style app bar in WPF?

I intended to create a Windows 8 Style App (Metro), but found out there is no support for using dual screen which is a demand for my app.
Now I am redesigning my app as a desktop application in WPF.
But I still like to mimic some nice design features from Windows 8 Apps.
One of the design features is the fly out bars typically used in a Windows 8 style app:
Bottom App bar for commands
Top Navigational bar
Right Charm that is common for all apps
The design they all have in common is a temporary flyout panel that is layered on top of the current window layout.
My question is: How can I create something similar in WPF?
I have no problem to create a main grid with a hidden bottom row that is made visible to display some common command buttons. But it would be nice to have it fly out on top of my standard layout, not squeeze it.
I know it is possible to open a new window on top of the current but that creates a bad code design and is hard to get nice looking. I would prefer to do it in the same window.
Cool question! I've actually done the charm bar fairly recently..
ideally what you need is something like
<Grid x:Name="LayoutRoot">
<Grid x:Name="Overlay" Panel.ZIndex="1000" Visibility="Collapsed">
<!-- This is where your slide out control is going to go -->
</Grid>
<!-- Use whatever layout you need -->
<ContentControl x:Name="MainContent" />
</Grid>
Now rather than squeezing the content - the Overlay grid will be on top of it similar to the charm bar! all with XAML
If you have anymore questions about this, give me a shout!
Edit; my Charm implementation - feel free to use for inspriation!
public class SlidePanel : ContentControl
{
static SlidePanel()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(SlidePanel), new FrameworkPropertyMetadata(typeof(SlidePanel)));
}
public SlidePanel()
{
EventManager.RegisterClassHandler(typeof(SlidePanel), SlidePanel.MouseEnterEvent,
new RoutedEventHandler(OnLocalMouseEnter));
EventManager.RegisterClassHandler(typeof(SlidePanel), SlidePanel.MouseLeaveEvent,
new RoutedEventHandler(OnLocalMouseLeave));
}
#region Mouse Handlers
private static void OnLocalMouseEnter(object sender, RoutedEventArgs e)
{
SetExpanded(sender, true);
}
private static void OnLocalMouseLeave(object sender, RoutedEventArgs e)
{
SetExpanded(sender, false);
}
private static void SetExpanded(object sender, bool expanded)
{
SlidePanel panel = sender as SlidePanel;
if (panel != null)
{
panel.IsExpanded = expanded;
}
}
#endregion Mouse Handlers
#region Panel Width
public double PanelWidth
{
get { return (double)GetValue(PanelWidthProperty); }
set { SetValue(PanelWidthProperty, value); }
}
// Using a DependencyProperty as the backing store for PanelWidth. This enables animation, styling, binding, etc...
public static readonly DependencyProperty PanelWidthProperty =
DependencyProperty.Register("PanelWidth", typeof(double), typeof(SlidePanel), new UIPropertyMetadata(5.0));
#endregion Panel Width
#region Closed Width
public double ClosedWidth
{
get { return (double)GetValue(ClosedWidthProperty); }
set { SetValue(ClosedWidthProperty, value); }
}
// Using a DependencyProperty as the backing store for ClosedWidth. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ClosedWidthProperty =
DependencyProperty.Register("ClosedWidth", typeof(double), typeof(SlidePanel), new UIPropertyMetadata(5.0, new PropertyChangedCallback(OnClosedWidthChange)));
#endregion Closed Width
#region Expanded Property
public bool IsExpanded
{
get { return (bool)GetValue(IsExpandedProperty); }
set { SetValue(IsExpandedProperty, value); }
}
// Using a DependencyProperty as the backing store for IsExpanded. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsExpandedProperty =
DependencyProperty.Register("IsExpanded", typeof(bool), typeof(SlidePanel), new UIPropertyMetadata(false, new PropertyChangedCallback(OnExpandedChanged)));
#endregion Expanded Property
#region Property Changes
private static void OnExpandedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue == e.OldValue)
return;
SlidePanel panel = d as SlidePanel;
if (panel == null)
return;
bool newVal = (bool)e.NewValue;
panel.IsExpanded = newVal;
bool expanded = (bool)panel.GetValue(IsExpandedProperty);
Storyboard widthAnimation = AnimationHelper.CreateDoubleAnimation<SlidePanel>(panel, expanded,
(p, a) =>
{
a.From = (double)p.GetValue(SlidePanel.ClosedWidthProperty);
a.To = (double)p.GetValue(SlidePanel.PanelWidthProperty);
},
(p, a) =>
{
a.From = (double)p.GetValue(SlidePanel.WidthProperty);
a.To = (double)p.GetValue(SlidePanel.ClosedWidthProperty);
}, new TimeSpan(0, 0, 0, 0, 300), WidthProperty);
Timeline opacity = AnimationHelper.DoubleAnimation(0.0, 1.0, expanded,
new TimeSpan(0, 0, 0, 0, 300), OpacityProperty);
Storyboard.SetTargetName(opacity, panel.Name);
Storyboard.SetTargetProperty(opacity, new PropertyPath(OpacityProperty));
widthAnimation.Children.Add(opacity);
widthAnimation.Begin(panel);
}
private static void OnClosedWidthChange(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
SlidePanel panel = d as SlidePanel;
if (panel != null)
panel.Width = (double)e.NewValue;
}
#endregion Property Changes
}
A little trick I found was to have the opacity set to 0 when it wasnt expanded but set the width to 10, this then allows the user to put the mouse at the side of the screen and then it will appear after a second or so..
cheers.
Edit - As requested.. AnimationHelper.
public class AnimationHelper
{
public static Timeline DoubleAnimation(double from, double to, bool modifier, TimeSpan duration, DependencyProperty property)
{
DoubleAnimation animation = new DoubleAnimation();
if (modifier)
{
animation.From = from;
animation.To = to;
}
else
{
animation.To = from;
animation.From = to;
}
animation.Duration = new Duration(duration);
return animation;
}
public static Storyboard CreateDoubleAnimation<T>(T control, bool modifier, double from, double to, TimeSpan duration, DependencyProperty property) where T : Control
{
return
AnimationHelper.CreateDoubleAnimation<T>(control, modifier,
(p, a) =>
{
a.From = from;
a.To = to;
},
(p, a) =>
{
a.From = to;
a.To = from;
}, duration, property);
}
public static Storyboard CreateDoubleAnimation<T>(T control, bool modifier, Action<T, DoubleAnimation> onTrue, Action<T, DoubleAnimation> onFalse, TimeSpan duration, DependencyProperty property) where T : Control
{
if (control == null)
throw new ArgumentNullException("control");
DoubleAnimation panelAnimation = new DoubleAnimation();
if (modifier)
{
if (onTrue != null)
onTrue.Invoke(control, panelAnimation);
}
else
{
if (onFalse != null)
onFalse.Invoke(control, panelAnimation);
}
panelAnimation.Duration = new Duration(duration);
Storyboard sb = new Storyboard();
Storyboard.SetTargetName(panelAnimation, control.Name);
Storyboard.SetTargetProperty(panelAnimation, new PropertyPath(property));
sb.Children.Add(panelAnimation);
return sb;
}
}

Frame ContentLoaded event

I'm new at Silverlight.
I've created a sort of master page using a Page with a frame where the content is loaded. As I handle multiple UserControls at the time (only one is shown, but I want to keep the state of the opened before) I'm setting Content property instead of Navigate method. That way I can assign a UserControl (already created, not a new one as it would be using Navigate with the Uri to the UserControl).
Now I want to take a picture as shown here from the frame when its content changes. If I do it immediately when the content set, the UserControl won't be shown in the picture because it takes a few secs. Frames have the event Navigated, but it doesn't fire with property Content (it just fires when the method Navigate is used, as it name says).
How can I know when new Content is loaded?
If it helps I'm using Silverligh 5.
I've a solution but I don't really like it, so I'm still looking for other ways.
public class CustomFrame : Frame
{
private readonly RoutedEventHandler loadedDelegate;
public static readonly DependencyProperty UseContentInsteadNavigationProperty =
DependencyProperty.Register("UseContentInsteadNavigation", typeof (bool), typeof (CustomFrame), new PropertyMetadata(true));
public bool UseContentInsteadNavigation
{
get { return (bool)GetValue(UseContentInsteadNavigationProperty); }
set { SetValue(UseContentInsteadNavigationProperty, value); }
}
public CustomFrame()
{
this.loadedDelegate = this.uc_Loaded;
}
public new object Content
{
get { return base.Content; }
set
{
if (UseContentInsteadNavigation)
{
FrameworkElement fe = (FrameworkElement)value;
fe.Loaded += loadedDelegate;
base.Content = fe;
}
else
{
base.Content = value;
}
}
}
void uc_Loaded(object sender, RoutedEventArgs e)
{
((UserControl)sender).Loaded -= loadedDelegate;
OnContentLoaded();
}
public delegate void ContentLoadedDelegate(Frame sender, EventArgs e);
public event ContentLoadedDelegate ContentLoaded;
private void OnContentLoaded()
{
if (ContentLoaded != null)
ContentLoaded(this, new EventArgs());
}
}

RibbonApplicationMenu: getting rid of the AuxiliaryPane

It so happened that the application I'm working on doesn't operate on documents, so there's no need in displaying the recently opened documents list in the application menu.
But - annoyingly - there are no properties readily available in the RibbonApplicationMenu class to hide the unused AuxiliaryPane (for which, curiously, the property does exist, but is marked as "internal").
Of course, I can just leave it there - but that's... untidy.
So, here's the solution I came up with.
Hope it will be helpful for anyone else :-)
The general idea is to subclass the RibbonApplicationMenu, find the template child corresponding to the menu's Popup, and overrule its Width (after a number of frustrating experiments it became evident that doing that neither for PART_AuxiliaryPaneContentPresenter nor for PART_FooterPaneContentPresenter - nor for the both - could achieve anything).
Well, without further ado, here's the code:
public class SlimRibbonApplicationMenu : RibbonApplicationMenu
{
private const double DefaultPopupWidth = 180;
public double PopupWidth
{
get { return (double)GetValue(PopupWidthProperty); }
set { SetValue(PopupWidthProperty, value); }
}
public static readonly DependencyProperty PopupWidthProperty =
DependencyProperty.Register("PopupWidth", typeof(double),
typeof(SlimRibbonApplicationMenu), new UIPropertyMetadata(DefaultPopupWidth));
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
this.DropDownOpened +=
new System.EventHandler(SlimRibbonApplicationMenu_DropDownOpened);
}
void SlimRibbonApplicationMenu_DropDownOpened(object sender, System.EventArgs e)
{
DependencyObject popupObj = base.GetTemplateChild("PART_Popup");
Popup popupPanel = (Popup)popupObj;
popupPanel.Width = (double)GetValue(PopupWidthProperty);
}
}
As a side note, I tried to find any way to resolve the desired width based on the max width of the ApplicationMenu's Items (rather than setting it explicitly through the DependencyProperty in XAML) - but to no avail.
Given my despise to "magic numbers", any suggestion on that will be deeply appreciated.
I know this has been a while, but I've got another solution to this. This one does not provide the Popup width property, instead a ShowAuxilaryPanel boolean. It then goes to Bind the width of the Popup, to the width of the menu item area of the menu.
public class SlimRibbonApplicationMenu : RibbonApplicationMenu
{
public bool ShowAuxilaryPanel
{
get { return (bool)GetValue(ShowAuxilaryPanelProperty); }
set { SetValue(ShowAuxilaryPanelProperty, value); }
}
public static readonly DependencyProperty ShowAuxilaryPanelProperty =
DependencyProperty.Register("ShowAuxilaryPanel", typeof(bool),
typeof(SlimRibbonApplicationMenu), new UIPropertyMetadata(true));
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
this.DropDownOpened += SlimRibbonApplicationMenu_DropDownOpened;
}
void SlimRibbonApplicationMenu_DropDownOpened(object sender, EventArgs e)
{
DependencyObject popupObj = base.GetTemplateChild("PART_Popup");
Popup panel = (Popup)popupObj;
var exp = panel.GetBindingExpression(Popup.WidthProperty);
if (!this.ShowAuxilaryPanel && exp == null)
{
DependencyObject panelArea = base.GetTemplateChild("PART_SubMenuScrollViewer");
var panelBinding = new Binding("ActualWidth")
{
Source = panelArea,
Mode = BindingMode.OneWay
};
panel.SetBinding(Popup.WidthProperty, panelBinding);
}
else if (this.ShowAuxilaryPanel && exp != null)
{
BindingOperations.ClearBinding(panel, Popup.WidthProperty);
}
}
}
worked for me
<telerik:ApplicationMenu RightPaneVisibility="Collapsed" >

Highlight Search TextBlock

My goal is to create a custom TextBlock control that has a new dependency property, SearchText. This property will contain a regular expression. All occurrences of this regular expression in the text of the TextBlock will be highlighted using a custom style (another DP).
My current implementation involves clearing all of the Inline objects in the TextBlock's InlineCollection. I then fill the TextBlock with runs for unhighlighted text and runs for highlighted text with the style applied (this method does not support adding inlines directly to the TextBlock, instead TextBlock.TextProperty has to be used).
Works great, but sometimes I get a strange exception when trying to clear the Inlines: InvalidOperationException: "Cannot modify the logical children for this node at this time because a tree walk is in progress."
This problem seems to be related to this one. I am modifying the inlines in the TextChanged function, but I'm using a flag to avoid infinite recursive edits.
Any thoughts on how to architect this custom control? Is there a better way to do this? How do I get around this exception?
Thanks!
In my implementation, I solved this by just adding another dependency property, called OriginalText. When it's modified, I updated both the Text property and update the highlighting. Here's the code:
public class HighlightTextBlock : TextBlock
{
public string HighlightedText
{
get { return (string)GetValue(HighlightedTextProperty); }
set { SetValue(HighlightedTextProperty, value); }
}
public static readonly DependencyProperty HighlightedTextProperty =
DependencyProperty.Register("HighlightedText", typeof(string), typeof(HighlightTextBlock), new UIPropertyMetadata(string.Empty, UpdateHighlightEffect));
public static readonly DependencyProperty OriginalTextProperty = DependencyProperty.Register(
"OriginalText", typeof(string), typeof(HighlightTextBlock), new PropertyMetadata(default(string), OnOriginalTextChanged));
private static void OnOriginalTextChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
var block = ((HighlightTextBlock)obj);
block.Text = block.OriginalText;
block.UpdateHighlightEffect();
}
public string OriginalText
{
get { return (string)GetValue(OriginalTextProperty); }
set { SetValue(OriginalTextProperty, value); }
}
private static void UpdateHighlightEffect(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
if (!(string.IsNullOrEmpty(e.NewValue as string) && string.IsNullOrEmpty(e.OldValue as string)))
((HighlightTextBlock)sender).UpdateHighlightEffect();
}
private void UpdateHighlightEffect()
{
if (string.IsNullOrEmpty(HighlightedText)) return;
var allText = GetCompleteText();
Inlines.Clear();
var indexOfHighlightString = allText.IndexOf(HighlightedText, StringComparison.InvariantCultureIgnoreCase);
if (indexOfHighlightString < 0)
{
Inlines.Add(allText);
}
else
{
Inlines.Add(allText.Substring(0, indexOfHighlightString));
Inlines.Add(new Run()
{
Text = allText.Substring(indexOfHighlightString, HighlightedText.Length),
Background = Consts.SearchHighlightColor,
});
Inlines.Add(allText.Substring(indexOfHighlightString + HighlightedText.Length));
}
}
private string GetCompleteText()
{
var allText = Inlines.OfType<Run>().Aggregate(new StringBuilder(), (sb, run) => sb.Append(run.Text), sb => sb.ToString());
return allText;
}
}
Still not sure if there's a better way to do this altogether, but I appear to have found a work around.
I was updating the inlines/runs in a function that was fired by the change notification for the TextProperty and the SearchTextProperty.
Now I'm firing the highlight/update code from a Dispatcher.BeginInvoke() call in the change notification with DispatcherPriority.Normal.
In case anyone wants an example of how to do this, I found this

Resources