WPF Drawing MultiLine Text on a canvas using the maximum font - wpf

I have several blocks of text that I need to add to a canvas for printing. They will all have a set width and height. They are all multiline and I would like the font to scale as large as possible. I have tried several things with a viewbox, however I can't seem to get the multiline and font scaling to work in unison.
<Viewbox Width="200" Height="200" Stretch="Uniform" >
<TextBox Text="Test test test test test test Test test test test test test "
TextWrapping="Wrap" AcceptsReturn="True" BorderThickness="0"></TextBox>
</Viewbox>
If I have somelike like the above, I get one line of text.

The only way I could see to do this was to start with a large font and scale it down until it fits.
double fontSize = 30;
tb.FontSize = fontSize;
while (CalculateIsTextTrimmed(tb))
{
fontSize--;
tb.FontSize = fontSize;
}
private static bool CalculateIsTextTrimmed(TextBlock textBlock)
{
Typeface typeface = new Typeface(
textBlock.FontFamily,
textBlock.FontStyle,
textBlock.FontWeight,
textBlock.FontStretch);
FormattedText formattedText = new FormattedText(
textBlock.Text,
System.Threading.Thread.CurrentThread.CurrentCulture,
textBlock.FlowDirection,
typeface,
textBlock.FontSize,
textBlock.Foreground);
formattedText.MaxTextWidth = textBlock.Width;
return (formattedText.Height > textBlock.Height);
}

Maybe a TextBlock would be better for presenting text-for-printing than a TextBox?
I tried implementing Jonah's solution. Inspired by This answer I wrapped it in a behavior (also note the addition of formattedText.Trimming = TextTrimming.None)
using System.Collections.Generic;
using System.ComponentModel;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Interactivity;
using System.Windows.Media;
namespace WpfApplication1
{
public class ScaleFontBehavior : Behavior<TextBlock>
{
// MaxFontSize
public static readonly DependencyProperty MaxFontSizeProperty = DependencyProperty.Register("MaxFontSize",
typeof (double), typeof (ScaleFontBehavior), new PropertyMetadata(20d));
// MinFontSize
public static readonly DependencyProperty MinFontSizeProperty = DependencyProperty.Register("MinFontSize",
typeof (double), typeof (ScaleFontBehavior), new PropertyMetadata(12d));
private readonly TextBlock dummy = new TextBlock();
public double MaxFontSize
{
get { return (double) GetValue(MaxFontSizeProperty); }
set { SetValue(MaxFontSizeProperty, value); }
}
public double MinFontSize
{
get { return (double) GetValue(MinFontSizeProperty); }
set { SetValue(MinFontSizeProperty, value); }
}
protected override void OnAttached()
{
DependencyPropertyDescriptor dpd = DependencyPropertyDescriptor.FromProperty(TextBlock.TextProperty,
typeof (TextBlock));
if (dpd != null)
{
dpd.AddValueChanged(AssociatedObject, delegate { CalculateFontSize(); });
}
AssociatedObject.SizeChanged += (s, e) => CalculateFontSize();
dummy.MaxWidth = AssociatedObject.MaxWidth;
dummy.TextWrapping = AssociatedObject.TextWrapping;
}
private void CalculateFontSize()
{
double fontSize = MaxFontSize;
AssociatedObject.FontSize = fontSize;
while (CalculateIsTextTrimmed(AssociatedObject))
{
fontSize--;
if (fontSize < MinFontSize) break;
AssociatedObject.FontSize = fontSize;
}
}
private static bool CalculateIsTextTrimmed(TextBlock textBlock)
{
var typeface = new Typeface(
textBlock.FontFamily,
textBlock.FontStyle,
textBlock.FontWeight,
textBlock.FontStretch);
var formattedText = new FormattedText(
textBlock.Text,
Thread.CurrentThread.CurrentCulture,
textBlock.FlowDirection,
typeface,
textBlock.FontSize,
textBlock.Foreground)
{ Trimming = TextTrimming.None };
formattedText.MaxTextWidth = textBlock.Width;
return (formattedText.Height > textBlock.Height);
}
}
class VisualHelper
{
public static List<T> FindVisualChildren<T>(DependencyObject obj) where T : DependencyObject
{
List<T> children = new List<T>();
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
var o = VisualTreeHelper.GetChild(obj, i);
if (o != null)
{
if (o is T)
children.Add((T)o);
children.AddRange(FindVisualChildren<T>(o)); // recursive
}
}
return children;
}
public static T FindUpVisualTree<T>(DependencyObject initial) where T : DependencyObject
{
DependencyObject current = initial;
while (current != null && current.GetType() != typeof(T))
{
current = VisualTreeHelper.GetParent(current);
}
return current as T;
}
}
}
Here's is an example of it's usage (type into the TextBox and see the text in the TextBlock scale to fit.
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:wpfApplication1="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<TextBox x:Name="TextBox" MaxLines="10" AcceptsReturn="True" />
<TextBlock Text="{Binding ElementName=TextBox, Path=Text}" Height="200" Width="200" TextWrapping="Wrap">
<i:Interaction.Behaviors>
<wpfApplication1:ScaleFontBehavior MaxFontSize="50" MinFontSize="12" />
</i:Interaction.Behaviors>
</TextBlock>
</StackPanel>
</Window>

check if this works:
<Canvas>
<Viewbox Width="200" Height="200" Stretch="Uniform" >
<StackPanel Width="200" Height="200" >
<TextBox Width="Auto" Text="Test test test test test " TextWrapping="Wrap" AcceptsReturn="True" ></TextBox>
<TextBox Width="Auto" Text="Test test test test test " TextWrapping="Wrap" AcceptsReturn="True" ></TextBox>
<TextBlock Width="Auto" Text="Test test test test test " TextWrapping="Wrap" ></TextBlock>
</StackPanel>
</Viewbox>
</Canvas>

Related

Custom WPF MessageBox does not return MessageBoxResult

This is the xaml "SidiMessageBoxWindow.xaml" file:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" mc:Ignorable="d">
<Border>
<Grid x:Name="mainGrid" Margin="0">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ScrollViewer Grid.Row="0" VerticalScrollBarVisibility="Auto" Margin="0">
<TextBlock x:Name="TextBlock" TextWrapping="Wrap" Text="TextBlock" ScrollViewer.VerticalScrollBarVisibility="Auto" Height="Auto"/>
</ScrollViewer>
<Grid Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Height="Auto" Width="Auto" Margin="0,10,0,0">
<Button x:Name="btnCancel" Content="Cancel" Width="Auto" MinWidth="0" Height="30"
HorizontalAlignment="Right" HorizontalContentAlignment="Center" VerticalAlignment="Center" VerticalContentAlignment="Center"
Margin="0,0,45,0" Padding="4,0,4,0" BorderThickness="1" />
<Button x:Name="btnOk" Content="Ok" Width="Auto" MinWidth="40" Height=" 30"
HorizontalAlignment="Right" HorizontalContentAlignment="Center" VerticalAlignment="Center" VerticalContentAlignment="Center"
Margin="0" Padding="4,0,4,0" BorderThickness="1" />
</Grid>
</Grid>
</Border>
and here the "SidiMessageBox1" class
I can't use "messageBox.ShowDialog();" here because I have to use the class "NTWindow" which I don't have access to.
public class SidiMessageBox1 : SidiMessageBoxWindow1
{
public static MessageBoxResult Show(ChartControl chartControl, string text, MessageBoxButton buttons = MessageBoxButton.OK)
{
if (chartControl == null)
{
return MessageBoxResult.None;
}
var messageBox = CreateMessageBox(chartControl, text, buttons);
messageBox.Show();
return messageBox.MsgBoxResult;
}
private static SidiMessageBoxWindow1 CreateMessageBox(ChartControl chartControl, string text, MessageBoxButton buttons)
{
return new SidiMessageBoxWindow1(text, buttons)
{
Owner = chartControl.OwnerChart,
Foreground = Application.Current.TryFindResource("FontControlBrush") as SolidColorBrush
};
}
}
and here is the SidiMessageBoxWindow1 class
public class SidiMessageBoxWindow1 : NTWindow
{
private static readonly string xamlFilePath = Path.Combine(Globals.UserDataDir, #"bin\Custom\AddOns\Sidi\SidiMessageBoxWindow.xaml");
private string text;
private Button btnOk, btnCancel;
private TextBlock textBlock;
private MessageBoxButton buttons;
public SidiMessageBoxWindow1()
{
}
public SidiMessageBoxWindow1(string text, MessageBoxButton buttons)
{
this.text = text;
Caption = "SidiMessageBox";
Topmost = true;
MinHeight = 100;
MinWidth = 200;
ResizeMode = ResizeMode.NoResize;
SizeToContent = SizeToContent.WidthAndHeight;
WindowStartupLocation = WindowStartupLocation.CenterOwner;
Content = LoadXaml(xamlFilePath);
Buttons = buttons;
}
private void OkButton_Click(object sender, RoutedEventArgs e)
{
btnOk.Click -= OkButton_Click;
Close();
}
private void CancelButton_Click(object sender, RoutedEventArgs e)
{
btnCancel.Click -= CancelButton_Click;
Close();
}
private DependencyObject LoadXaml(string xmlFilePath)
{
Window page;
FileStream fs = new FileStream(xmlFilePath, FileMode.Open);
page = (Window)XamlReader.Load(fs);
btnOk = LogicalTreeHelper.FindLogicalNode(page, "btnOk") as Button;
btnCancel = LogicalTreeHelper.FindLogicalNode(page, "btnCancel") as Button;
textBlock = LogicalTreeHelper.FindLogicalNode(page, "TextBlock") as TextBlock;
textBlock.Text = text;
return page.Content as DependencyObject;
}
public MessageBoxButton Buttons
{
get
{
return buttons;
}
set
{
buttons = value;
btnCancel.Visibility = Visibility.Collapsed;
btnOk.Visibility = Visibility.Collapsed;
switch (buttons)
{
case MessageBoxButton.OK:
btnOk.Visibility = Visibility.Visible;
btnOk.Click += OkButton_Click;
break;
case MessageBoxButton.OKCancel:
btnCancel.Visibility = Visibility.Visible;
btnOk.Visibility = Visibility.Visible;
btnOk.Click += OkButton_Click;
btnCancel.Click += CancelButton_Click;
break;
}
}
}
public MessageBoxResult MsgBoxResult { get; set; }
}
call:
var result = SidiMessageBox1.Show(ChartControl, "text");
the messageboxwindow looks like this:
everything works fine, as it should, except that I don't get a "MessageBoxResult" back. Unfortunately I don't know how to do that with this code.
I thank "BionicCode" for his explanation and hope for your understanding, because i am still quite a beginner ;-)
To use the Thread class is considered an obsolete programming model. Since the introduction of async and await with .NET Framework 4.5 the recommended programming model is the Microsoft Docs: Task asynchronous programming model.
As the member name Dispatcher.InvokeAsync suggests, this method is awaitable and supports asynchronous execution.
Your code actually does not execute the Window on a new thread. Because you use post related code to the Dispatcher, the Window is shown on the main thread.
Showing another Window will not block the other Window instances.
Additionally, your posted code is quite smelly. You should never block a constructor. But showing a modal dialog from a constructor will block construction. A constructor must initialize the members and return immediately.
Instead you must create and show the Window instance from your static Show method:
public static MessageBoxResult Show(ChartControl chartControl, string text, MessageBoxButton buttons)
{
if (chartControl == null)
{
return MessageBoxResult.None;
}
SidiMessageBoxWindow messageBox = CreateMessageBox(chartControl, text, buttons);
// If just an OK button, allow the user to just move away from the dialog
if (buttons == MessageBoxButton.OK)
{
messageBox.Show();
}
else
{
messageBox.ShowDialog();
}
return messageBox.MsgBoxResult;
}
private static SidiMessageBoxWindow CreateMessageBox(ChartControl chartControl, string text, MessageBoxButton buttons)
{
return new SidiMessageBoxWindow(xamlFilePath, logFilePath, text, buttons, chartControl)
{
Owner = chartControl.OwnerChart,
Foreground = Application.Current.TryFindResource("FontControlBrush") as SolidColorBrush
};
}
private SidiMessageBoxWindow(ChartControl chartControl, string text, MessageBoxButton buttons)
{
this.chartControl = chartControl;
this.text = text;
this.buttons = buttons;
}

Can anyone provide a concrete example of WPF "visual inheritance" for a dialog box?

I am an experienced WinForms developer, relatively new to WPF. I have a large WinForms application that uses a couple different base classes to represent dialog boxes. One such example is AbstractOkCancelDialog. That class contains a panel at the bottom of a dialog, with an Ok and Cancel button on the right side of the panel. I'm trying to determine the best way to handle this, as I realize that WPF doesn't provide visual inheritance.
I don't want to have to create OK and Cancel buttons, and place them, for every dialog in the application.
I have read that the way to do this in WPF is with user controls. I can envision creating a user control with OK and Cancel buttons on it. But I don't want to have to manually place that user control on hundreds of dialogs in my application. I'd really like to have something like this:
public AbstractOkCancelDialog = class(Window)
{
protected AbstractOkCancelDialogViewModel _ViewModel;
// AbstractOkCancelDialogViewModel would have commands for OK and Cancel.
// Every dialog would inherit from AbstractOkCancelDialog, and would use
// a viewmodel that inherits from AbstractOkCancelDialogViewModel. In
// this way, all view models would automatically be connected to the OK
// and Cancel commands.
}
I've seen some discussion online about how to create the base class. Those discussions explain how there can't be a xaml file associated with the dialog base class, and I understand that restriction. I just can't figure out how to automatically place the user control with the OK and Cancel buttons.
I'm hoping that someone can point me to a sample solution that shows this kind of structure. Thank you in advance!
Write one dialog class. It's a subclass of Window. It has XAML:
<Window
...blah blah blah...
Title="{Binding Title}"
>
<StackPanel MinWidth="300">
<!-- This is how you place content within content in WPF -->
<ContentControl
Content="{Binding}"
Margin="2"
/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="2,20,2,2">
<Button
Margin="2"
MinWidth="60"
DockPanel.Dock="Right"
Content="OK"
Click="OK_Click"
IsDefault="True"
/>
<Button
Margin="2"
MinWidth="60"
DockPanel.Dock="Right"
Content="Cancel"
IsCancel="True"
Click="Cancel_Click"
/>
</StackPanel>
</StackPanel>
</Window>
You can fancy that up endlessly, but this is a decent minimum to give you arbitrary content above a row of right-aligned buttons. Adding more buttons as needed could involve either templating that portion of the window as well, or creating them with an ItemsControl (I've done that in our production code), or a few other options.
Usage:
var vm = new SomeDialogViewModel();
var dlg = new MyDialog { DataContext = vm };
For each dialog viewmodel, consumers must define an implicit datatemplate which provides UI for that viewmodel.
I would suggest writing a dialog viewmodel interface which the consumer is expected to implement.
public interface IDialogViewModel
{
String Title { get; set; }
void OnOK();
// Let them "cancel the cancel" if they like.
bool OnCancel();
}
The window can check if its DataContext implements that interface, and act accordingly. If you like, it could require that interface and throw an exception of it isn't implemented, or it could just talk to it only if it's there. If they don't implement it but they still have a Title property, the binding to Title will still work. Bindings are "duck typed".
Naturally, you can write an OKCancelDialogViewModel or a SelectStringFromListViewModel, write corresponding DataTemplates that implement their UIs, and write nice clean static methods which show them:
public static class Dialogs
{
public static TOption Select<TOption>(IEnumerable<TOption> options, string prompt,
string title = "Select Option") where TOption : class
{
// Viewmodel isn't generic because that breaks implicit datatemplating.
// That's OK because XAML uses duck typing anyhow.
var vm = new SelectOptionDialogViewModel
{
Title = title,
Prompt = prompt,
Options = options
};
if ((bool)new Dialog { DataContext = vm }.ShowDialog())
{
return vm.SelectedOption as TOption;
}
return null;
}
// We have to call the value-type overload by a different name because overloads can't be
// distinguished when the only distinction is a type constraint.
public static TOption? SelectValue<TOption>(IEnumerable<TOption> options, string prompt,
string title = "Select Option") where TOption : struct
{
var vm = new SelectOptionDialogViewModel
{
Title = title,
Prompt = prompt,
// Need to box these explicitly
Options = options.Select(opt => (object)opt)
};
if ((bool)new Dialog { DataContext = vm }.ShowDialog())
{
return (TOption)vm.SelectedOption;
}
return null;
}
}
Here's a viewmodel datatemplate for the above selection dialog:
<Application.Resources>
<DataTemplate DataType="{x:Type local:SelectOptionDialogViewModel}">
<StackPanel>
<TextBlock
TextWrapping="WrapWithOverflow"
Text="{Binding Prompt}"
/>
<ListBox
ItemsSource="{Binding Options}"
SelectedItem="{Binding SelectedOption}"
MouseDoubleClick="ListBox_MouseDoubleClick"
/>
</StackPanel>
</DataTemplate>
</Application.Resources>
App.xaml.cs
private void ListBox_MouseDoubleClick(object sender,
System.Windows.Input.MouseButtonEventArgs e)
{
((sender as FrameworkElement).DataContext as IDialogViewModel).DialogResult = true;
}
var a = Dialogs.Select(new String[] { "Bob", "Fred", "Ginger", "Mary Anne" },
"Select a dance partner:");
var b = Dialogs.SelectValue(Enum.GetValues(typeof(Options)).Cast<Options>(),
"Select an enum value:");
Here an example of how to use a custom AlertDialog
UserControl
<UserControl x:Class="Library.Views.AlertMessageDialogView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:p="clr-namespace:Library.Properties"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
FlowDirection = "{Binding WindowFlowDirection, Mode=TwoWay}">
<Grid Background="{DynamicResource WindowBackgroundBrush}">
<Canvas HorizontalAlignment="Left" Height="145" VerticalAlignment="Top" Width="385">
<Label HorizontalAlignment="Left" Height="57" VerticalAlignment="Top" Width="365" Canvas.Left="10" Canvas.Top="10" FontSize="14" >
<TextBlock x:Name="txtVocabAnglais" TextWrapping="Wrap" Text="{Binding Message, Mode=TwoWay}" Width="365" Height="57" />
</Label>
<Button x:Name="cmdCancel" Content="{x:Static p:Resources.AlertMessageDialogViewcmdCancel}" Height="30" Canvas.Left="163" Canvas.Top="72" Width="71" Command = "{Binding CancelCommand}" CommandParameter = "null" IsDefault="True"/>
</Canvas>
</Grid>
</UserControl>
ViewModel Class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Windows;
using System.ComponentModel;
using System.Windows.Controls;
namespace Library.ViewModel
{
public class AlertMessageDialogViewModel : BindableBaseViewModel
{
public event EventHandler CloseWindowEvent;
private string _title;
private string _message;
public BaseCommand<string> YesCommand { get; private set; }
public BaseCommand<string> CancelCommand { get; private set; }
private WinformsNameSpace.FlowDirection _windowFlowDirection;
public AlertMessageDialogViewModel()
{
CancelCommand = new BaseCommand<string>(cmdCancelBtnClick);
WindowFlowDirection = CustomFuncVar.WindowFlowDirection;
}
public WinformsNameSpace.FlowDirection WindowFlowDirection
{
get
{
return _windowFlowDirection;
}
set
{
_windowFlowDirection = value;
OnPropertyChanged("WindowFlowDirection");
}
}
public string Message
{
get
{
return _message;
}
set
{
_message = value;
OnPropertyChanged("Message");
}
}
public string Title
{
get
{
return _title;
}
set
{
_title = value;
}
}
private void cmdCancelBtnClick(string paramerter)
{
if (CloseWindowEvent != null)
CloseWindowEvent(this, null);
}
}
}
DialogMessage Class
using System;
using System.Windows;
using System.Collections.Generic;
namespace Library.Helpers
{
public static class DialogMessage
{
public static void AlertMessage(string message, string title, Window OwnerWindowView)
{
try
{
//Affichage de méssage de succès enregistrement
AlertMessageDialogViewModel alertDialogVM = new AlertMessageDialogViewModel();
alertDialogVM.Message = message;
alertDialogVM.Title = title;
// Auto Generation Window
FrameworkElement view = LpgetCustomUI.AutoGetViewFromName("AlertMessageDialogView");
view.DataContext = alertDialogVM;
Dictionary<string, object> localVarWindowProperty = new Dictionary<string, object>();
localVarWindowProperty = LpgetCustomUI.GetWindowPropretyType_400x145(Properties.Resources.ApplicationTitle);
CummonUIWindowContainer alertDialogView = new CummonUIWindowContainer(view, null, false, localVarWindowProperty);
//End Auto Generation Window
// Attachement de l'évènement de fermture de View au modèle
alertDialogVM.CloseWindowEvent += new EventHandler(alertDialogView.fnCloseWindowEvent);
if (OwnerWindowView!=null)
{
alertDialogView.Owner = OwnerWindowView;
}
else
{
alertDialogView.WindowStartupLocation = WindowStartupLocation.CenterScreen;
}
alertDialogView.ShowDialog();
}
catch (Exception ex)
{
}
}
}
}
CummonUIWindowContainer Class
namespace CummonUILibrary.CummonUIHelpers
{
public class CummonUIWindowContainer : Window
{
public event RoutedEventHandler CmbRootEvtLanguageChange;
private FrameworkElement currentView;
private ContentControl _contentcontainer;
public CummonUIWindowContainer(string usercontrolName)
{
Contentcontainer = new ContentControl();
currentView = new FrameworkElement();
}
public CummonUIWindowContainer()
{
Contentcontainer = new ContentControl();
currentView = new FrameworkElement();
}
public CummonUIWindowContainer(FrameworkElement view, object model, bool setDataContextToView, Dictionary<string, object> WindowPropertyList)
{
Contentcontainer = new ContentControl();
Contentcontainer.Name = "ContentControl";
SetWindowProperty(view, model, setDataContextToView, WindowPropertyList);
}
public void SetWindowProperty(FrameworkElement view, object model, bool setDataContextToView, Dictionary<string, object> WindowPropertyList)
{
try
{
LinearGradientBrush brush = new LinearGradientBrush();
GradientStop gradientStop1 = new GradientStop();
gradientStop1.Offset = 0;
gradientStop1.Color = Colors.Yellow;
brush.GradientStops.Add(gradientStop1);
GradientStop gradientStop2 = new GradientStop();
gradientStop2.Offset = 0.5;
gradientStop2.Color = Colors.Indigo;
brush.GradientStops.Add(gradientStop2);
GradientStop gradientStop3 = new GradientStop();
gradientStop3.Offset = 1;
gradientStop3.Color = Colors.Yellow;
brush.GradientStops.Add(gradientStop3);
this.Background = brush;
CurrentView = view;
Type elementType = this.GetType();
ICollection<string> WindowPropertyListNames = WindowPropertyList.Keys;
foreach (string propertyName in WindowPropertyListNames)
{
PropertyInfo property = elementType.GetProperty(propertyName);
property.SetValue(this, WindowPropertyList[propertyName]);
}
if (setDataContextToView == true & model != null)
{
CurrentView.DataContext = model;
}
if (CurrentView != null)
{
Contentcontainer.Content = CurrentView;
}
//Contentcontainer.Margin = new Thickness(0,0, 0, 0);
IAddChild container=this;
container.AddChild(Contentcontainer);
}
catch (Exception ex)
{
}
}
public void fnCloseWindowEvent(object sender, EventArgs e)
{
this.Close();
}
public ContentControl Contentcontainer
{
get
{
return _contentcontainer;
}
set
{
_contentcontainer = value;
}
}
public FrameworkElement CurrentView
{
get
{
return currentView;
}
set
{
if (this.currentView != value)
{
currentView = value;
//RaisePropertyChanged("CurrentView");
}
}
}
private void cmbLanguage_SelectionChanged(object sender, RoutedEventArgs e)
{
//CmbRootEvtLanguageChange(sender, e);
}
}
}
How to use the Class
DialogMessage.AlertMessage("My Custom Message", "My Custom Title Message");
Thats how i would do it
Create an abstract base class for your dialog and changing the corresponding ControlTemplate
AbstractOkCancelDialog
public abstract class AbstractOkCancelDialog : Window
{
public static readonly DependencyProperty CancelCommandParameterProperty =
DependencyProperty.Register(
"CancelCommandParameter",
typeof(object),
typeof(AbstractOkCancelDialog),
new FrameworkPropertyMetadata((object) null));
public static readonly DependencyProperty CancelCommandProperty =
DependencyProperty.Register(
"CancelCommand",
typeof(ICommand),
typeof(AbstractOkCancelDialog),
new FrameworkPropertyMetadata((ICommand) null));
public static readonly DependencyProperty OkCommandParameterProperty =
DependencyProperty.Register(
"OkCommandParameter",
typeof(object),
typeof(AbstractOkCancelDialog),
new FrameworkPropertyMetadata((object) null));
public static readonly DependencyProperty OkCommandProperty =
DependencyProperty.Register(
"OkCommand",
typeof(ICommand),
typeof(AbstractOkCancelDialog),
new FrameworkPropertyMetadata((ICommand) null));
static AbstractOkCancelDialog()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(AbstractOkCancelDialog), new
FrameworkPropertyMetadata(typeof(AbstractOkCancelDialog)));
}
public ICommand CancelCommand
{
get => (ICommand) GetValue(CancelCommandProperty);
set => SetValue(CancelCommandProperty, value);
}
public object CancelCommandParameter
{
get => GetValue(CancelCommandParameterProperty);
set => SetValue(CancelCommandParameterProperty, value);
}
public ICommand OkCommand
{
get => (ICommand) GetValue(OkCommandProperty);
set => SetValue(OkCommandProperty, value);
}
public object OkCommandParameter
{
get => GetValue(OkCommandParameterProperty);
set => SetValue(OkCommandParameterProperty, value);
}
}
Style
Put in Generic.xaml[?]
<Style
BasedOn="{StaticResource {x:Type Window}}"
TargetType="{x:Type local:AbstractOkCancelDialog}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:AbstractOkCancelDialog}">
<Border
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<AdornerDecorator>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ContentPresenter />
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button
Grid.Column="1"
Margin="5"
Command="{TemplateBinding OkCommand}"
CommandParameter="{TemplateBinding OkCommandParameter}"
Content="Ok"
DockPanel.Dock="Right" />
<Button
Grid.Column="2"
Margin="5"
Command="{TemplateBinding CancelCommand}"
CommandParameter="{TemplateBinding CancelCommandParameter}"
Content="Cancel"
DockPanel.Dock="Right" />
</Grid>
</Grid>
</AdornerDecorator>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Now you can create your individual dialogs like you would create any other window
Brief example:
TestDialog.xaml
<local:AbstractOkCancelDialog
x:Class="WpfApp.TestDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfApp"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="TestDialog"
Width="800"
Height="450"
OkCommand="{x:Static local:Commands.OkWindowCommand}"
OkCommandParameter="{Binding RelativeSource={RelativeSource Self}}"
CancelCommand="{x:Static local:Commands.CancelWindowCommand}"
CancelCommandParameter="{Binding RelativeSource={RelativeSource Self}}"
mc:Ignorable="d">
<Grid>
<!-- Content -->
</Grid>
</local:AbstractOkCancelDialog>
TestDialog.xaml.cs
public partial class TestDialog : AbstractOkCancelDialog
{
...
}

Is it possible to change caret width for TextBox in silverlight when overwrite mode?

I have the TextBox in silverlight 4 in 2 modes: insert and overwrite.
Can anyone help me? If I press the overwrite mode, I want to make the caret blinking size bigger.
I have used to CaretBrush, but it can change the color of caret only.
Many thanks if you have suggestion or the sample code.
At the time looking someone can help, I tried this solution for myself, It's abit complex, but I hope someone can use my code and customize yourself. Someone can get this code for trying if you have this case
The MainPage.xaml code:
<UserControl x:Class="SilverlightApplication1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.micros`enter code here`oft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Grid Name="LayoutRoot">
<Canvas x:Name="canvasDataView" VerticalAlignment="Top" Background="White" HorizontalAlignment="Left">
<TextBox HorizontalAlignment="Left" Canvas.Left="50" VerticalAlignment="Top" x:Name="positionTextBox" Canvas.ZIndex="2" Text="position" Canvas.Top="25" Visibility="Collapsed" Width="100" Height="25"></TextBox>
<TextBox x:Name="textBox1" Text="asdfasdfasd asda asdf as adfasdfads adf asdf adfasdf" SelectionForeground="AliceBlue" KeyDown="textBox1_KeyDown" KeyUp="textBox1_KeyUp" TextWrapping="NoWrap" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Visible" AcceptsReturn="True" Height="25" Width="500" />
<TextBox x:Name="shadowTextBox" Canvas.ZIndex="-1" Canvas.Left="350" Height="20" Width="20" HorizontalScrollBarVisibility="Visible" AcceptsReturn="True" VerticalScrollBarVisibility="Visible" Visibility="Visible" TextWrapping="NoWrap"/>
</Canvas>
</Grid>
</UserControl>
The code behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Expression.Interactivity;
using System.Windows.Controls.Primitives;
namespace SilverlightApplication1
{
public partial class MainPage : UserControl
{
Duration duration = new Duration(TimeSpan.FromSeconds(0.5));
Storyboard storybroad = new Storyboard();
Rectangle myRectangle = null;
ScrollViewer sv;
public MainPage()
{
InitializeComponent();
textBox1.KeyUp += new KeyEventHandler(textBox1_KeyUp);
textBox1.TextChanged += new TextChangedEventHandler(textBox1_TextChanged);
textBox1.CaretBrush = new SolidColorBrush(Colors.Transparent);
}
void textBox1_TextChanged(object sender, TextChangedEventArgs e)
{
shadowTextBox.Text = textBox1.Text;
shadowTextBox.SelectionStart = textBox1.SelectionStart;
Point p = getCaretPosition(textBox1, shadowTextBox);
MoveCaret(p);
}
void textBox1_KeyDown(object sender, KeyEventArgs e)
{
shadowTextBox.Text = textBox1.Text;
shadowTextBox.SelectionStart = textBox1.SelectionStart;
}
void textBox1_KeyUp(object sender, KeyEventArgs e)
{
shadowTextBox.Text = textBox1.Text;
shadowTextBox.SelectionStart = textBox1.SelectionStart;
// get caret position
Point p = getCaretPosition(textBox1,shadowTextBox);
MoveCaret(p);
}
private Point getCaretPosition(TextBox textBox, TextBox shadowTextBox)
{
Point _point = new Point();
// get main textbox's scroll offset, if any
getScrollBar(textBox);
double initVerticalOffset = sv.VerticalOffset;
double initHorizontalOffset = sv.HorizontalOffset;
// get shadow box scroll offset
getScrollBar(shadowTextBox);
double vOffset = sv.VerticalOffset;
double hOffset = sv.HorizontalOffset;
// caret position is scroll offset of shadaw minus scroll offset of main (if any)
_point.Y = vOffset - initVerticalOffset;
_point.X = hOffset - initHorizontalOffset;
return _point;
}
private void getScrollBar(UIElement src)
{
// walk visual tree for this object until we get the scrollviewer
if (src.GetType().ToString() == "System.Windows.Controls.ScrollViewer")
sv = src as ScrollViewer;
else
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(src); i++)
{
UIElement elem = (UIElement)VisualTreeHelper.GetChild(src, i);
getScrollBar(elem);
}
}
}
void MoveCaret(Point point)
{
if (storybroad == null)
storybroad = new Storyboard();
storybroad.Stop();
if (point != new Point(0, 0))
{
if (myRectangle != null && canvasDataView.Children.Contains(myRectangle))
{
canvasDataView.Children.Remove(myRectangle);
}
myRectangle = new Rectangle();
myRectangle.SetValue(Canvas.TopProperty, point.Y);
myRectangle.SetValue(Canvas.LeftProperty, point.X);
myRectangle.Width = 5;
myRectangle.Height = 16;
myRectangle.Fill = new SolidColorBrush();
storybroad.BeginTime = new TimeSpan(0, 0, 0, 0, 0);
storybroad.RepeatBehavior = RepeatBehavior.Forever;
ColorAnimation color = new ColorAnimation();
canvasDataView.Children.Add(myRectangle);
color.From = Colors.Transparent;
color.To = Colors.Green;
color.Duration = duration;
storybroad.Children.Add(color);
Storyboard.SetTarget(color, myRectangle);
Storyboard.SetTargetProperty(color, new PropertyPath("(Fill).(SolidColorBrush.Color)"));
storybroad.Begin();
}
}
}
}

Speed of Color Resolution in XAML (Silverlight)

Is there anyway to speed-test which of the following resolve faster in XAML for colors:
Named Color: Orange
Hex: #ff6600
Shorthand Hex: #f60
Known Color: DarkOrange
This is not just a curiosity or an academic exercise. I have tons and tons of animations and colors that change colors many many times, on a large scale. I need to eek out ever bit of time-saving I can.
Looking for a way to test the above against each other for Silverlight. Any ideas?
I couldn't think of a definitive way to test this, but I put something together that may give you a hint. Effectively, I bound the color of a rectangle to a (string) SelectedColor property of a class that raised the PropertyChanged event every time it changed. Then I created four different List collections that had colors defined in all the different ways that you described, and in a background thread looped through them each some 10000 times, setting the SelectedColor property on each loop by dispatching to back to the UI thread. The complicated part was just keeping all the threads synchronized properly, and there's at least one hack in there where I loop every 20 ms until I saw that the previous task had finished. But it at least forces the UI thread to parse a color string, which is presumably what you're interested in.
At any rate, to the extent that this testing methodology is valid, it looks like using the "hex" notation is perhaps slightly faster than the others, though not by much. The average results on my machine after 10 test runs:
Hex: 4596 ms
Named: 4609 ms
Shorthand Hex: 5018 ms
Known Colors: 5065 ms
Just for reference, here's the code-behind:
public partial class MainPage : UserControl, INotifyPropertyChanged
{
public MainPage()
{
InitializeComponent();
namedColors.Add("Black");
namedColors.Add("Black");
namedColors.Add("Brown");
namedColors.Add("Cyan");
namedColors.Add("DarkGray");
namedColors.Add("Gray");
namedColors.Add("Green");
namedColors.Add("LightGray");
namedColors.Add("Magenta");
namedColors.Add("Orange");
hexColors.Add(Colors.Black.ToString());
hexColors.Add(Colors.Black.ToString());
hexColors.Add(Colors.Brown.ToString());
hexColors.Add(Colors.Cyan.ToString());
hexColors.Add(Colors.DarkGray.ToString());
hexColors.Add(Colors.Gray.ToString());
hexColors.Add(Colors.Green.ToString());
hexColors.Add(Colors.LightGray.ToString());
hexColors.Add(Colors.Magenta.ToString());
hexColors.Add(Colors.Orange.ToString());
knownColors.Add("LawnGreen");
knownColors.Add("LemonChiffon");
knownColors.Add("LightBlue");
knownColors.Add("LightCoral");
knownColors.Add("LightCyan");
knownColors.Add("LightGoldenrodYellow");
knownColors.Add("LightGray");
knownColors.Add("LightGreen");
knownColors.Add("LightPink");
knownColors.Add("LightSalmon");
shorthandHexColors.Add("#000");
shorthandHexColors.Add("#111");
shorthandHexColors.Add("#222");
shorthandHexColors.Add("#333");
shorthandHexColors.Add("#444");
shorthandHexColors.Add("#555");
shorthandHexColors.Add("#666");
shorthandHexColors.Add("#777");
shorthandHexColors.Add("#aaa");
shorthandHexColors.Add("#bbb");
LayoutRoot.DataContext = this;
}
private List<string> namedColors = new List<string>();
private List<string> hexColors = new List<string>();
private List<string> shorthandHexColors = new List<string>();
private List<string> knownColors = new List<string>();
private List<double> namedColorDurations = new List<double>();
private List<double> hexColorDurations = new List<double>();
private List<double> shorthandHexColorDurations = new List<double>();
private List<double> knownColorDurations = new List<double>();
private string selectedColor;
public string SelectedColor
{
get
{
return selectedColor;
}
set
{
if (selectedColor != value)
{
selectedColor = value;
RaisePropertyChanged("SelectedColor");
}
}
}
private void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
private bool isTesting = false;
private void testButton_Click(object sender, RoutedEventArgs e)
{
if (isTesting)
{
return;
}
else
{
isTesting = true;
}
ThreadPool.QueueUserWorkItem(o =>
{
AutoResetEvent resetEvent = new AutoResetEvent(false);
for (int i = 0; i < 10; i++)
{
TestColors(resetEvent, hexColors, lstHexColorDuration, hexColorDurations);
resetEvent.WaitOne();
TestColors(resetEvent, namedColors, lstNamedColorDuration, namedColorDurations);
resetEvent.WaitOne();
TestColors(resetEvent, shorthandHexColors, lstShorthandHexDuration, shorthandHexColorDurations);
resetEvent.WaitOne();
TestColors(resetEvent, knownColors, lstKnownColorDuration, knownColorDurations);
resetEvent.WaitOne();
}
Dispatcher.BeginInvoke(() =>
{
lstHexColorDuration.Items.Add(hexColorDurations.Average().ToString());
lstNamedColorDuration.Items.Add(namedColorDurations.Average().ToString());
lstShorthandHexDuration.Items.Add(shorthandHexColorDurations.Average().ToString());
lstKnownColorDuration.Items.Add(knownColorDurations.Average().ToString());
});
isTesting = false;
});
}
private int testsFinished = 0;
private void TestColors(AutoResetEvent resetEvent, List<string> colorList, ListBox resultListBox, List<double> results)
{
ThreadPool.QueueUserWorkItem(o =>
{
var start = DateTime.Now;
int testPasses = 10000;
testsFinished = 0;
for (int i = 0; i < testPasses; i++)
{
foreach (string color in colorList)
{
SetColor(color);
}
}
while (testsFinished < testPasses * colorList.Count)
{
Thread.Sleep(20);
}
DateTime finish = DateTime.Now;
results.Add((finish - start).TotalMilliseconds);
Dispatcher.BeginInvoke(() => resultListBox.Items.Add((DateTime.Now - start).ToString()));
resetEvent.Set();
});
}
private void SetColor(string color)
{
Dispatcher.BeginInvoke(() =>
{
SelectedColor = color;
Interlocked.Increment(ref testsFinished);
});
}
}
And here's the XAML proper:
<UserControl
x:Class="SilverlightScratch.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SilverlightScratch"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="500">
<Grid x:Name="LayoutRoot" Background="White" >
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Button Height="30" Width="100" x:Name="testButton" Content="Start Test" Click="testButton_Click" />
<Rectangle Height="30" Width="100" Fill="{Binding SelectedColor}" Grid.Row="2" />
<StackPanel Orientation="Horizontal" Grid.Row="1">
<StackPanel>
<TextBlock Text="Named Color Duration" />
<ListBox x:Name="lstNamedColorDuration" />
</StackPanel>
<StackPanel>
<TextBlock Text="Hex Color Duration" />
<ListBox x:Name="lstHexColorDuration" />
</StackPanel>
<StackPanel>
<TextBlock Text="Shorthand Hex Duration" />
<ListBox x:Name="lstShorthandHexDuration" />
</StackPanel>
<StackPanel>
<TextBlock Text="Known Colors Duration" />
<ListBox x:Name="lstKnownColorDuration" />
</StackPanel>
</StackPanel>
</Grid>
</UserControl>

WPF Template: AdornedElement not showing!

I want to add some elements to a TextBox by using a Template with the extra elements and the original TextBox inserted in the right spot. I'm trying to use the AdornedElementPlaceholder just like you would do when making a Validation.ErrorTemplate But the AdornedElement is not showing up. I have simplified the example as much as possible:
<TextBox Text="Border me with my Template">
<TextBox.Template>
<ControlTemplate TargetType="{x:Type TextBox}">
<Border BorderBrush="Green" BorderThickness="1">
<AdornedElementPlaceholder/>
</Border>
</ControlTemplate>
</TextBox.Template>
</TextBox>
The result is just a green box around the space that should be my textbox!
Unfortunately, that's just not how it works. However, it's not much more difficult than that. If you want to add a green border around a text box by changing the control template, it's going to look a bit more like this:
<ControlTemplate TargetType="TextBox">
<Border x:Name="Border"
CornerRadius="2"
Background="{TemplateBinding Background}"
BorderBrush="Green"
BorderThickness="1"
SnapsToDevicePixels="True">
<ScrollViewer x:Name="PART_ContentHost"/>
</Border>
</ControlTemplate>
The important part in there is the ScrollViewer named PART_ContentHost. The TextBox looks for that guy when the template is applied and uses it to host the text, caret, etc. What I think you're missing is that when you override the Template for an element, you're overriding the entire control template. It's unfortunate that you can't just change bits and pieces, but that's the truth of the matter.
Of course, if you want to maintain the original look and feel of the TextBox, such that it still looks like a Win7 TextBox for instance, you'd need to do a bit more in the ControlTemplate.
For what it's worth, it looks like the template you were trying to apply would work if you're talking about using an Adorner. It's similar to how the validation templates work in WPF, but that's a whole-nother story.
Oh, and for a much simpler way to change the border to green, you should be able to just set the BorderBrush property on the TextBox itself. Of course, I really don't know exactly what you're going for.
--
HTH
Dusty
I ended up doing a Custom Control based on a HeaderedContent Control.
It will allow the user to click or hoover over some content and then show a bubble containing some other or the same content.
Usage:
<JsCustomControls:BaloonBox LabelText="{Binding Note}" WaterMark="Click to write Note" Type="ClickToShow">
<JsCustomControls:BaloonBox.Content>
<TextBox AcceptsReturn="True" Text="{Binding Path=Note}" TextWrapping="Wrap"></TextBox>
</JsCustomControls:BaloonBox.Content>
</JsCustomControls:BaloonBox>
The code for the User control:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
namespace JsCustomControls
{
public enum BaloonBoxTypeEnum
{
ClickToShow,
MouseOverToShow,
Manual
}
[TemplatePart(Name = BaloonBox.HeaderElement, Type = typeof(ContentPresenter))]
[TemplatePart(Name = BaloonBox.ContentElement, Type = typeof(ContentPresenter))]
[TemplatePart(Name = BaloonBox.PopUpElement, Type = typeof(Popup))]
[TemplatePart(Name=BaloonBox.LabelElement,Type=typeof(Label))]
public class BaloonBox : HeaderedContentControl
{
DispatcherTimer PopupTimer = new DispatcherTimer();
private const string HeaderElement = "PART_HeaderContentControl";
private const string ContentElement = "PART_ContenContentControl";
private const string PopUpElement = "PART_PopUp";
private const string LabelElement = "PART_HeaderLabel";
private ContentPresenter headerContentControl;
private ContentPresenter contentContentControl;
private Popup popUp;
private Label headerLabel;
static BaloonBox()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(BaloonBox), new FrameworkPropertyMetadata(typeof(BaloonBox)));
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
headerContentControl = GetTemplateChild(HeaderElement) as ContentPresenter;
contentContentControl = GetTemplateChild(ContentElement) as ContentPresenter;
popUp = GetTemplateChild(PopUpElement) as Popup;
headerLabel = GetTemplateChild(LabelElement) as Label;
if (headerContentControl != null) headerContentControl.MouseDown += new MouseButtonEventHandler(headerContentControl_MouseDown);
if(headerLabel!=null)headerLabel.MouseDown+=new MouseButtonEventHandler(headerContentControl_MouseDown);
if (headerContentControl != null) headerContentControl.MouseMove += new MouseEventHandler(headerContentControl_MouseMove);
if(headerLabel!=null)headerLabel.MouseMove+=new MouseEventHandler(headerContentControl_MouseMove);
PopupTimer = new DispatcherTimer();
PopupTimer.Tick += new EventHandler(PopupTimer_Tick);
if(string.IsNullOrEmpty(LabelText))
{
if (headerLabel != null) headerLabel.Foreground = Brushes.Gray;
if (headerLabel != null) headerLabel.Content = WaterMark;
}
else
{
if (headerLabel != null) headerLabel.Foreground = Brushes.Black;
if (headerLabel != null) headerLabel.Content = LabelText;
}
}
void headerContentControl_MouseMove(object sender, MouseEventArgs e)
{
if (Type == BaloonBoxTypeEnum.MouseOverToShow)
{
if (popUp != null) popUp.IsOpen = true;
PopupTimer.Interval = new TimeSpan(0, 0, 0, 2);
PopupTimer.Start();
}
}
void headerContentControl_MouseDown(object sender, MouseButtonEventArgs e)
{
if (Type == BaloonBoxTypeEnum.ClickToShow)
{
if (popUp != null) popUp.IsOpen = true;
PopupTimer.Interval = new TimeSpan(0, 0, 0, 3);
PopupTimer.Start();
}
}
void PopupTimer_Tick(object sender, EventArgs e)
{
if (!headerContentControl.IsMouseOver && !contentContentControl.IsMouseOver && !contentContentControl.IsKeyboardFocusWithin)
{
PopupTimer.Stop();
popUp.IsOpen = false;
}
}
public static readonly DependencyProperty IsOpenProperty = DependencyProperty.Register("IsOpen",
typeof (bool),
typeof (BaloonBox),
new FrameworkPropertyMetadata
(new PropertyChangedCallback
(OnIsOpenChanged)));
public bool IsOpen
{
get { return (bool) GetValue(IsOpenProperty); }
set{SetValue(IsOpenProperty,value);}
}
private static void OnIsOpenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
BaloonBox baloonBox = (BaloonBox)d;
baloonBox.popUp.IsOpen =(bool)e.NewValue;
}
public static readonly DependencyProperty WaterMarkProperty = DependencyProperty.Register("WaterMark",
typeof (string),
typeof (BaloonBox),
new FrameworkPropertyMetadata
(new PropertyChangedCallback
(OnWatermarkChanged)));
public string WaterMark
{
get { return (string)GetValue(WaterMarkProperty); }
set { SetValue(WaterMarkProperty,value); }
}
private static void OnWatermarkChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
public static readonly DependencyProperty LabelTextProperty = DependencyProperty.Register("LabelText",
typeof (string),
typeof (BaloonBox)
,new FrameworkPropertyMetadata(new PropertyChangedCallback(OnLabelTextChanged)));
public string LabelText
{
get { return (string) GetValue(LabelTextProperty); }
set
{
SetValue(LabelTextProperty,value);
headerLabel.Content = value;
}
}
private static void OnLabelTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
BaloonBox baloonBox = (BaloonBox)d;
if (string.IsNullOrEmpty(e.NewValue.ToString()))
{
if (baloonBox.headerLabel != null) baloonBox.headerLabel.Foreground = Brushes.Gray;
if (baloonBox.headerLabel != null) baloonBox.headerLabel.Content = baloonBox.WaterMark;
}
else
{
if (baloonBox.headerLabel != null) baloonBox.headerLabel.Foreground = Brushes.Black;
if (baloonBox.headerLabel != null) baloonBox.headerLabel.Content = e.NewValue;
}
}
public static readonly DependencyProperty BaloonBoxTypeProperty = DependencyProperty.Register(
"BaloonBoxType", typeof(BaloonBoxTypeEnum), typeof(BaloonBox), new FrameworkPropertyMetadata(new PropertyChangedCallback(OnBaloonBoxTypeChanged)));
public BaloonBoxTypeEnum Type
{
get { return (BaloonBoxTypeEnum) GetValue(BaloonBoxTypeProperty);}
set { SetValue(BaloonBoxTypeProperty,value);}
}
private static void OnBaloonBoxTypeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
//who cares?
}
}
}
</Border>
<Popup x:Name="PART_PopUp" HorizontalOffset="-50" VerticalOffset="-5" AllowsTransparency="True" IsOpen="False" PopupAnimation="Slide" PlacementTarget="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Header}">
<Grid Height="150" Width="250" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2"></ColumnDefinition>
<ColumnDefinition Width="0.050*"></ColumnDefinition>
<ColumnDefinition Width="0.900*"></ColumnDefinition>
<ColumnDefinition Width="0.050*"></ColumnDefinition>
<ColumnDefinition Width="2"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="2"></RowDefinition>
<RowDefinition Height="0.300*"></RowDefinition>
<RowDefinition Height="0.700*"></RowDefinition>
<RowDefinition Height="0.100*"></RowDefinition>
<RowDefinition Height="2"></RowDefinition>
</Grid.RowDefinitions>
<Path Grid.Row="1" Grid.RowSpan="3" Grid.Column="1" Grid.ColumnSpan="3" Data="M79,279 L119,279 135,263 135,279 319,279 319,368.5 78.5,368.5 z" Fill="#FFFDFDB3" Stretch="Fill" Stroke="Black" UseLayoutRounding="False">
</Path>
<ScrollViewer Grid.Row="2" Grid.Column="2" Grid.RowSpan="1" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<ContentPresenter x:Name="PART_ContenContentControl" Content="{Binding Content, RelativeSource={RelativeSource TemplatedParent}}"/>
</ScrollViewer>
</Grid>
</Popup>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Resources