Custom ContextMenu for images in a RichTextBox - wpf

Users can embed images to a RichTextBox - I add them in an InlineUIContainer.
I've added a custom ContextMenu to the InlineUIContainer, but when right-clicked
the standard RTB-contextmenu (cut, copy, paste) appears - not the custom one.
Why, and how can I fix this?
string fileName = openFileDialog.FileName;
BitmapImage bitmap = new BitmapImage(new Uri(fileName, UriKind.Absolute));
Image image = new Image();
image.Source = bitmap;
image.Width = bitmap.Width;
image.Height = bitmap.Height;
InlineUIContainer pix = new InlineUIContainer(image, rt.CaretPosition);
pix.BaselineAlignment = BaselineAlignment.Center;
pix.ContextMenu = (ContextMenu)this.Resources["imageContext"];

Right, this works - I had to recreate the all the "standard" context menu items, including the spellchecking ones (fortunately I found an example of that : )
private void rt_ContextMenuOpening(object sender, ContextMenuEventArgs e)
{
int index = 0;
MenuItem menuItem;
this.rt.ContextMenu.Items.Clear(); //Clearing the existing items
// right clicked on an image ?
int offZstart, offZend;
activeImg = null;
foreach (Block block in rt.Document.Blocks)
{
Paragraph p = block as Paragraph;
if (p != null)
{
foreach (Inline inline in p.Inlines)
{
InlineUIContainer iuic = inline as InlineUIContainer;
if (iuic != null)
{
offZstart = rt.Selection.Start.GetOffsetToPosition(iuic.ContentStart);
offZend = rt.Selection.End.GetOffsetToPosition(iuic.ContentEnd);
// if (rt.Selection.Contains(iuic.ContentStart))
if ((offZstart == 2 || offZstart == 1) && (offZend == -2 || offZend == -1))
{
if (iuic.Child is Border) // I wrap my images in borders
{
Border border = (Border)iuic.Child;
activeImg = (Image)border.Child;
MenuItem imageMenu = new MenuItem();
imageMenu.Header = "Image..";
this.rt.ContextMenu.Items.Insert(index++, imageMenu);
MenuItem borderItem = new MenuItem();
borderItem.Header = "Border";
borderItem.Click += imgBorderWidth;
imageMenu.Items.Add(borderItem);
MenuItem marginItem = new MenuItem();
marginItem.Header = "Margin";
marginItem.Click += imgMarginWidth;
imageMenu.Items.Add(marginItem);
MenuItem paddingItem = new MenuItem();
paddingItem.Header = "Padding";
paddingItem.Click += imgPaddingWidth;
imageMenu.Items.Add(paddingItem);
}
}
}
}
}
}
// spellchecking
SpellingError spellingError = this.rt.GetSpellingError(this.rt.CaretPosition);
if (spellingError != null && spellingError.Suggestions.Count() >= 1)
{
//Creating the suggestions menu items.
foreach (string suggestion in spellingError.Suggestions)
{
menuItem = new MenuItem();
menuItem.Header = suggestion;
menuItem.FontWeight = FontWeights.Bold;
menuItem.Command = EditingCommands.CorrectSpellingError;
menuItem.CommandParameter = suggestion;
menuItem.CommandTarget = this.rt;
this.rt.ContextMenu.Items.Insert(index++, menuItem);
}
//Getting the word to add/ignore
var word = this.rt.GetSpellingErrorRange(this.rt.CaretPosition);
this.rt.ContextMenu.Items.Insert(index++, new Separator());
//Adding the IgnoreAll menu item
MenuItem IgnoreAllMenuItem = new MenuItem();
IgnoreAllMenuItem.Header = "Ignore All ''"+word.Text+"''";
IgnoreAllMenuItem.Command = EditingCommands.IgnoreSpellingError;
IgnoreAllMenuItem.CommandTarget = this.rt;
this.rt.ContextMenu.Items.Insert(index++, IgnoreAllMenuItem);
this.rt.ContextMenu.Items.Insert(index++, new Separator());
//Add as new word in dictionary
MenuItem AddToDictionary = new MenuItem();
AddToDictionary.Header = "Add ''" + word.Text + "'' to dictionary";
AddToDictionary.Command = EditingCommands.IgnoreSpellingError;
AddToDictionary.CommandTarget = this.rt;
AddToDictionary.Click += (object o, RoutedEventArgs rea) =>
{
//this.AddToDictionary(word.Text);
MessageBox.Show("Want to add ''" + word.Text + "'' to dictionary\n- but don't know how..");
};
this.rt.ContextMenu.Items.Insert(index++, AddToDictionary);
this.rt.ContextMenu.Items.Insert(index++, new Separator());
}
//Cut
MenuItem cutMenuItem = new MenuItem();
cutMenuItem.Command = ApplicationCommands.Cut;
this.rt.ContextMenu.Items.Insert(index++, cutMenuItem);
//Copy
MenuItem copyMenuItem = new MenuItem();
copyMenuItem.Command = ApplicationCommands.Copy;
this.rt.ContextMenu.Items.Insert(index++, copyMenuItem);
//Paste
MenuItem pasteMenuItem = new MenuItem();
pasteMenuItem.Command = ApplicationCommands.Paste;
this.rt.ContextMenu.Items.Insert(index++, pasteMenuItem);
this.rt.ContextMenu.Items.Insert(index++, new Separator());
//Delete
MenuItem deleteMenuItem = new MenuItem();
deleteMenuItem.Command = EditingCommands.Delete;
this.rt.ContextMenu.Items.Insert(index++, deleteMenuItem);
this.rt.ContextMenu.Items.Insert(index++, new Separator());
//Select All
MenuItem selectAllMenuItem = new MenuItem();
selectAllMenuItem.Command = ApplicationCommands.SelectAll;
this.rt.ContextMenu.Items.Insert(index++, selectAllMenuItem);
}

Related

Adding a dynamically created panel to a Flowlayoutpanel on a user control

I'm working on a todo list application and I'm dynamically building a panel from another form based on user input, this said panel is suppose to be added to the flowlayoutpanel on a user control, but nothing is showing up.
Any ideas what might cause the problem.
Update: my code does work if it is executed directly from the user control itself.
My Code
private void btnCreateTask_Click_1(object sender, EventArgs e)
{
Panel pnlParentTask = new Panel();
pnlParentTask.Size = new Size(234, 89);
pnlParentTask.BorderStyle = BorderStyle.FixedSingle;
pnlParentTask.Location = new Point(217, 186);
PictureBox pbFinish = new PictureBox();
pbFinish.Size = new Size(22, 21);
pbFinish.Location = new Point(13, 13);
pbFinish.BorderStyle = BorderStyle.FixedSingle;
Label lblTitle = new Label();
lblTitle.Text = txtTaskName.Text;
lblTitle.ForeColor = Color.Black;
lblTitle.AutoSize = true;
lblTitle.Location = new Point(41, 12);
lblTitle.Font = new Font("Arial", 12f);
lblTitle.BackColor = Color.Transparent;
lblTitle.Tag = lblTitle.Text;
enc.setpanelTag(lblTitle.Text);
pnlParentTask.Tag = lblTitle.Text;
Label lblDescription = new Label();
lblDescription.Text = txtDescription.Text;
lblDescription.AutoSize = true;
lblDescription.Location = new Point(4, 38);
lblDescription.BackColor = Color.Transparent;
lblDescription.Font = new Font("Microsoft Yahei", 8.25f);
lblDescription.Tag = lblTitle.Text;
PictureBox pbPriority = new PictureBox();
pbPriority.Location = new Point(207, 3);
pbPriority.Tag = lblTitle.Text;
pbPriority.Size = new Size(22, 21);
pbPriority.BorderStyle = BorderStyle.FixedSingle;
Label lblDateCreated = new Label();
lblDateCreated.Location = new Point(3, 69);
lblDateCreated.Text = txtStartDate.Text;
lblDateCreated.BackColor = Color.Transparent;
lblDateCreated.Font = new Font("Microsoft Yahei", 8.25f);
lblDateCreated.Tag = lblTitle.Text;
Label lblDeleteTask = new Label();
lblDeleteTask.Text = "-";
lblDeleteTask.Font = new Font("Microsoft Yahei", 24f);
lblDeleteTask.Location = new Point(206, 53);
lblDeleteTask.Click += new EventHandler(lblRemoveTask_click);
pnlParentTask.Controls.Add(pbFinish);
pnlParentTask.Controls.Add(lblTitle);
pnlParentTask.Controls.Add(lblDescription);
pnlParentTask.Controls.Add(pbPriority);
pnlParentTask.Controls.Add(lblDateCreated);
pnlParentTask.Controls.Add(lblDeleteTask);
priority.flpParent.Controls.Add(pnlParentTask);
this.Close();
}

WPF prevent TreeViewItem to inherit contextMenu from parent by code

I have the following code and I set explicite the ContextMenu from a TreeViewItem to null but it shows all the time the same ContextMenu as the treeviewitem parents! Is that a bug? and how can I solve that?
Tree.Items.Clear();
System.Windows.Controls.TreeViewItem ceo = new System.Windows.Controls.TreeViewItem() { Header = "CEO" };
System.Windows.Controls.TreeViewItem manager1 = new System.Windows.Controls.TreeViewItem() { Header = "Manager1" };
System.Windows.Controls.TreeViewItem manager2 = new System.Windows.Controls.TreeViewItem() { Header = "Manager2" };
System.Windows.Controls.TreeViewItem person1 = new System.Windows.Controls.TreeViewItem() { Header = "person1" };
System.Windows.Controls.TreeViewItem person2 = new System.Windows.Controls.TreeViewItem() { Header = "person2" };
manager1.Items.Add(person1);
manager2.Items.Add(person2);
ceo.Items.Add(manager1);
ceo.Items.Add(manager2);
Tree.Items.Add(ceo);
// context Menu
MenuItem menu1 = new MenuItem();
menu1.Header = "Menu1";
MenuItem menu2 = new MenuItem();
menu2.Header = "Menu2";
MenuItem menu3 = new MenuItem();
menu3.Header = "Menu3";
MenuItem menu4 = new MenuItem();
menu4.Header = "Menu4";
MenuItem menu5 = new MenuItem();
menu5.Header = "Menu5";
MenuItem menu6 = new MenuItem();
menu6.Header = "Menu6";
System.Windows.Controls.ContextMenu ceoMenu = new ContextMenu();
ceoMenu.Items.Add(menu1);
ceoMenu.Items.Add(menu2);
ceoMenu.Items.Add(menu3);
System.Windows.Controls.ContextMenu managerMenu = new ContextMenu();
managerMenu.Items.Add(menu4);
managerMenu.Items.Add(menu5);
ceo.ContextMenu = ceoMenu;
manager1.ContextMenu = managerMenu;
manager2.ContextMenu = managerMenu;
System.Windows.Controls.ContextMenu personMenu = new ContextMenu();
personMenu.Items.Add(menu6);
person1.ContextMenu = personMenu;
person2.ContextMenu = null;
It works fine to set it like:
System.Windows.Controls.ContextMenu nullMenu = new ContextMenu();
nullMenu.Visibility = Visibility.Hidden;
person1.ContextMenu = personMenu;
person2.ContextMenu = nullMenu;
It's not a bug. If you set breakpoint on ContextMenuOpening event for person2, you will see, that it doesn't fired at all.
After that you can try to set breakpoint on ContextMenuOpening event for manager2. It's fired and on person2 node, but only because TreeViewItem is ItemsControl.
In WPF Visualizer we can see that:
It's not a bug. It is logical behavior. For example, if user clicks at the column at DataGrid, then it always shows the same ContextMenu. So if you set ContextMenu to null, then it just replaces your context menu by base context menu.
So we can make a little trick. We just change state of visibility of your ContextMenu.
So to have the personal Visibility of ContextMenu, you should just create another state of Visibility(Hidden, Visible or Collapse) to your created ContextMenu.
System.Windows.Controls.ContextMenu personMenu = new ContextMenu();
personMenu.Items.Add(menu6);
personMenu.Visibility = System.Windows.Visibility.Hidden;
person1TVI.ContextMenu = personMenu;
person2TVI.ContextMenu = personMenu;

WPF UserControl background Transparency doesn't work

I try to pop up a usercontrol using own class ModalDialogManager : Control but the background doesn't seem transparent as shown on http://prntscr.com/6cgyc2
in my ModalDialogManager class, I did specify the windows to allow transparency:
Window w = new Window();
m_window = w;
w.Closing += w_Closing;
w.Owner = GetParentWindow(this);
w.DataContext = this.DataContext;
w.SetBinding(Window.ContentProperty, "");
w.Title = Title;
w.Icon = Icon;
w.Height = DialogHeight;
w.Width = DialogWidth;
w.ResizeMode = DialogResizeMode;
// SHOULD IT WORK?!
w.AllowsTransparency = true;
double t = GetParentWindow(this).Left;
if (IsBorderless)
{
w.WindowStyle = WindowStyle.None;
w.ShowInTaskbar = false;
if (IsStartUpLocationCenter)
{
w.WindowStartupLocation = WindowStartupLocation.CenterScreen;
}
else
{
w.Left = LeftPosition;
w.Top = RightPosition;
w.WindowStartupLocation = WindowStartupLocation.Manual;
}
}
else
{
w.WindowStartupLocation = WindowStartupLocation.CenterOwner;
}
if (IsModal)
w.ShowDialog();
else
w.Show();
but the UserControl design shows ok: http://prntscr.com/6cgyz8
Thus, I tried to use a window to attach the usercontrol and do window.showDialog() with a usercontrol as below:
Window w = new Window();
SolidColorBrush b = new SolidColorBrush();
b.Color = .Colors.Transparent;
w.Background = b;
Grid g = new Grid();
g.Children.Add(new ucSelectCloth());
w.Content = g;
g.Background = b;
w.Height = 300;
w.Width = 600;
w.ShowDialog();
As you can see that http://prntscr.com/6cgzdk the window doesn't look Transparent too.
Any thought?
window remains opaque until you set Window.AllowsTransparency property to true. Once you set Window.AllowsTransparency to true you have to set WindowStyle to None.
add following lines to your code,
w.WindowStyle = System.Windows.WindowStyle.None;
w.AllowsTransparency = true;

GridSplitter Not present

I use this in code behind to test how to add a grid splitter programmatically. (I know, don't use code behind - But this is one of those rare cases I need to. (I think))
public partial class ContainerView : Window, IContainerView
{
[ImportingConstructor]
public ContainerView()
{
InitializeComponent();
SetUp();
}
public void SetUp()
{
_grid = new Grid();
//Single column/single row
_grid.ColumnDefinitions.Add(new ColumnDefinition());
_grid.ColumnDefinitions.Add(new ColumnDefinition());
_grid.ColumnDefinitions.Add(new ColumnDefinition());
_grid.RowDefinitions.Add(new RowDefinition());
_grid.RowDefinitions.Add(new RowDefinition());
var button1 = new Button();
button1.Content = "Btn 1";
button1.Margin = new Thickness(5);
Grid.SetRow(button1, 0);
Grid.SetColumn(button1, 0);
var button2 = new Button();
button2.Content = "Btn 2";
button2.Margin = new Thickness(5);
Grid.SetRow(button2, 1);
Grid.SetColumn(button2, 2);
_grid.Children.Add(button1);
_grid.Children.Add(button2);
var splitterV = new GridSplitter();
Grid.SetRowSpan(splitterV, _grid.RowDefinitions.Count);
splitterV.VerticalAlignment = VerticalAlignment.Stretch;
splitterV.HorizontalAlignment = HorizontalAlignment.Right;
splitterV.ShowsPreview = true;
splitterV.Background = Brushes.Black;
Width = 5;
_grid.Children.Add(splitterV);
Grid.SetColumn(splitterV, 1);
Content = _grid;
}
I can see the two buttons, but the middle column is empty. The GridSplitter is not shown. What am I doing wrong?
You are absolutely correct but you are setting Window's width instead of GridSpitter's width & also you have to give it's ResizeBehaviour.
Instead of this :
Width = 5;
Set GridSpitter's Width & It's ResizeBehavior as :
splitterV.ResizeBehavior = GridResizeBehavior.PreviousAndNext;
splitterV.Width = 5;

Using a Storyboard animation on a programmatically-added control

I'm trying to fade in a new control to my application's "app" area which is programmatically added after the existing controls are removed. My code looks like this:
void settingsButton_Clicked(object sender, EventArgs e)
{
ContentCanvas.Children.Clear();
// Fade in settings panel
NameScope.SetNameScope(this, new NameScope());
SettingsPane s = new SettingsPane();
s.Name = "settingsPane";
this.RegisterName(s.Name, s);
this.Resources.Add(s.Name, s);
Storyboard sb = new Storyboard();
DoubleAnimation settingsFade = new DoubleAnimation();
settingsFade.From = 0;
settingsFade.To = 1;
settingsFade.Duration = new Duration(TimeSpan.FromSeconds(0.33));
settingsFade.RepeatBehavior = new RepeatBehavior(1);
Storyboard.SetTargetName(settingsFade, s.Name);
Storyboard.SetTargetProperty(settingsFade, new PropertyPath(UserControl.OpacityProperty));
ContentCanvas.Children.Add(s);
sb.Children.Add(settingsFade);
sb.Begin();
}
However, when I run this code, I get the error "No applicable name scope exists to resolve the name 'settingsPane'."
What am I possibly doing wrong? I'm pretty sure I've registered everything properly :(
I wouldn't hassle with the NameScopes etc. and would rather use Storyboard.SetTarget instead.
var b = new Button() { Content = "abcd" };
stack.Children.Add(b);
var fade = new DoubleAnimation()
{
From = 0,
To = 1,
Duration = TimeSpan.FromSeconds(5),
};
Storyboard.SetTarget(fade, b);
Storyboard.SetTargetProperty(fade, new PropertyPath(Button.OpacityProperty));
var sb = new Storyboard();
sb.Children.Add(fade);
sb.Begin();
I solved the problem using this as parameter in the begin method, try:
sb.Begin(this);
Because the name is registered in the window.
I agree, the namescopes are probably the wrong thing to use for this scenario. Much simpler and easier to use SetTarget rather than SetTargetName.
In case it helps anyone else, here's what I used to highlight a particular cell in a table with a highlight that decays to nothing. It's a little like the StackOverflow highlight when you add a new answer.
TableCell cell = table.RowGroups[0].Rows[row].Cells[col];
// The cell contains just one paragraph; it is the first block
Paragraph p = (Paragraph)cell.Blocks.FirstBlock;
// Animate the paragraph: fade the background from Yellow to White,
// once, through a span of 6 seconds.
SolidColorBrush brush = new SolidColorBrush(Colors.Yellow);
p.Background = brush;
ColorAnimation ca1 = new ColorAnimation()
{
From = Colors.Yellow,
To = Colors.White,
Duration = new Duration(TimeSpan.FromSeconds(6.0)),
RepeatBehavior = new RepeatBehavior(1),
AutoReverse = false,
};
brush.BeginAnimation(SolidColorBrush.ColorProperty, ca1);
It is possible odd thing but my solution is to use both methods:
Storyboard.SetTargetName(DA, myObjectName);
Storyboard.SetTarget(DA, myRect);
sb.Begin(this);
In this case there is no error.
Have a look at the code where I have used it.
int n = 0;
bool isWorking;
Storyboard sb;
string myObjectName;
UIElement myElement;
int idx = 0;
void timer_Tick(object sender, EventArgs e)
{
if (isWorking == false)
{
isWorking = true;
try
{
myElement = stackObj.Children[idx];
var possibleIDX = idx + 1;
if (possibleIDX == stackObj.Children.Count)
idx = 0;
else
idx++;
var myRect = (Rectangle)myElement;
// Debug.WriteLine("TICK: " + myRect.Name);
var dur = TimeSpan.FromMilliseconds(2000);
var f = CreateVisibility(dur, myElement, false);
sb.Children.Add(f);
Duration d = TimeSpan.FromSeconds(2);
DoubleAnimation DA = new DoubleAnimation() { From = 1, To = 0, Duration = d };
sb.Children.Add(DA);
myObjectName = myRect.Name;
Storyboard.SetTargetName(DA, myObjectName);
Storyboard.SetTarget(DA, myRect);
Storyboard.SetTargetProperty(DA, new PropertyPath("Opacity"));
sb.Begin(this);
n++;
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message + " " + DateTime.Now.TimeOfDay);
}
isWorking = false;
}
}

Resources