I'm custom drawing a menu item in a MenuStrip. The problem I'm having is that the menu item insists on sizing itself based on the text, which is not what I want (there is no text). I can set AutoSize to false and explicitly specify a size, but the containing menu (ToolStripDropDown) still sizes itself based on the text, which causes it to be too small to contain the entire menu item.
Is there a straightforward way to set the size of a menu item?
It can be achieved by adding an empty image to the toolStripItem. Now change the size of image to give the desired size to your ToolStripItem. I have done this in my project as below:
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
public partial class Form1 : Form
{
public Form1()
{
Bitmap emptyImage = new Bitmap(48, 48);
ToolStripMenuItem m_Item = new ToolStripMenuItem(System.Convert.ToString("All Programs",emptyImage);
m_Item.ImageAlign = ContentAlignment.MiddleLeft;
m_Item.ImageScaling = ToolStripItemImageScaling.None;
m_Item.Name = btnId.ToString();
MenuStrip menu = new MenuStrip();
menu.Items.Add(m_Item);
}
}
This comments in an article on CodeProject explains the nature of my issue:
While you can handle the Paint event for a ToolStripMenuItem, ToolStripMenuItem isn't intended to be "owner drawn". If you want to handle the drawing of a particular tool strip item, the recommended means is to create your own ToolStripItem-derived type. See ToolStripItem Class[^] for an example.
You can set width (not height) of your menu item by using spaces, and then draw on empty space with OnPaint
If you need to place picture into drop down why use ToolStripMenuItem? For example you can place ToolStripControlHost with Image embedded. And do not forget that you can change drop down layout by using LayoutStyle and LayoutSettings properties (for example from stack to table layout)
You can not only specify size for your menu item, but you can also specify size for drop down where it located. For example when drop down is opening (OnOpening, Opening, DropDownOpending many ways to react) you can set minimum width (or height, or both) by using ToolStripDropDown.MinimumSize property.
In general ToolStrip is most properly architectured control in the WinForms namespace. It has almost unlimited possibilities and very extensible.
Update: According to your comment. I cannot say much, because while ToolStrip is most architectured it is also commented very good, and many particular things need to be discovered. I still added #3 to my answer, but many things can be discovered only by trying, and by using Reflector of course.
Related
I need to develop a simple WPF application. In the UI window, There are Labels and Text Blocks towards the left and Buttons towards the right.
Figure 1
Based on a config setting (whether the user is left-handed or right-handed) I need to switch the controls, Buttons towards the left and Labels and Text Blocks towards the right.
Figure 2
Can you please recommend a good way to address this requirement?
Depends what the scope of the app is likely to be.
2 alternatives:
1)
I think it likely as an app grows that there will be more than just buttons.
I would probably build a usercontrol which encapsulates this behaviour for a label and control. The usercontrol uses a static to decide where the textblocks are positioned but would look something like the row edit control in this:
https://gallery.technet.microsoft.com/WPF-Entity-Framework-MVVM-78cdc204
Which is a usercontrol has a contentpresenter in it so you can put any control you like ( such as a button ) "in" it and set a dependency property for the label.
2)
Define 2 contentcontrol templates similar to the one used in this:
https://social.technet.microsoft.com/wiki/contents/articles/28597.aspx
Put them in separate resource dictionaries and give them the same key.
Merge into application.current.resources the appropriate resource dictionary and hence style.
Seeing as this is an app setting, this is presumably a start up thing. People don't just change their "handedness" dynamically. So you could probably use these as staticresource. If they're realistically going to change at run time then I think this would be a bit more involved because you'd need to force re render of a view.
2 Templates are probably the right and stylish solution here as #RajN said.
Also you can define a grid with 2 columns and switch the property 'Grid.Column' of each controls accordingly
Maybe not the best way, but I managed to achieve this using a grid as per your suggestions. Thank you all for your valuable feedback.
I switched the columns and changed the widths accordingly.
if (AppSettings.IsLeft)
{
parentGrid.ColumnDefinitions[0].Width = new GridLength(400, GridUnitType.Pixel);
parentGrid.ColumnDefinitions[1].Width = new GridLength(1, GridUnitType.Star);
Grid.SetColumn(buttonGrid,0);
Grid.SetRow(buttonGrid,0);
Grid.SetColumn(contentGrid,1);
Grid.SetRow(contentGrid,0);
}
I have a simple user control (just an example): it is 40x100, but resizable. It has two buttons, one anchored at the top, one anchored at the bottom.
It put this control on a form and stretch it to 40x400. This works fine.
But as soon as I switch the form to Localizable = True and change the language to translate any strings, the Designer shows the user control
as if it was 40x100 for both the default an the translated language, i.e. the bottom button is not anchored.
Or better: the bottom button is displayed as if it was not anchored. The control occupies the correct amount of space (40x400), though (see selection highlight). And it displays fine during runtime, this is just a Designer issue.
A picture showing the issue.
Did I miss something here? Is this how it is supposed to work?
Im on VS2010 at the moment, tried the old VS2005 but it's the same there.
Thanks...
I could easily repro this problem by anchoring the second button to the bottom. The Anchor property has a few oddish failure modes, layout isn't always recalculated when it should be. You found one such case. I think the underlying issue is that the Size property is a localizable property as well and the designer fails to fire the required events when it starts a new localization set. Something like that, nothing very trivial.
You'll need to punt this problem and not rely on the Anchor property to get the button positioned correctly. That just takes a one-liner in your UserControl code, like:
protected override void OnResize(EventArgs e) {
button2.Top = this.ClientSize.Height - button2.Height;
base.OnResize(e);
}
I'm using the Winforms PropertyGrid; the target of the SelectedObject includes a property of type Image. Everything is fine, except that with all items the same height, the image is too small to see properly. I'd like to have some control over the height of grid items such that the image can be displayed a bit larger. One other detail is that the SelectedObject of one PropertyGrid control may be assigned an object of any of a variety of different classes (which may or may not have image properties), so I'm hoping the height can be driven by data in the instance of the SelectedObject itself, rather than making it a static behavior of the control, although I'd settle for a custom attribute of the image property to make the item height at least class-specific if it can't be instance-specific.
How can I do this? Custom attribute? PropertyGrid event? Something else?
As Simon commented on your question, this is not possible to have a custom height for a GridItem.
You have 2 solutions to be able to show an image with a reasonable size:
You can code your own UITypeEditor. That way, the user would just click the down arrow and see a nicely sized image in the dropdown box.
Sorry for the plug but I think it directly answers your question: only 3rd party PropertyGrids may allow you to get variable size rows in the grid. Smart PropertyGrid.Net is one of them. You set a HeightMultiplier to the row so that it expands on let's say 4 rows. Then you code your own Look class that handles the drawing of the image the way you want in this space.
I'm creating a program where the width of column needs to be relatively narrow, but when I add a combo box I have to increase the width so that I can read the listings in the drop down. I know that in HTML the drop down adjust to the length of longest entry, in Iron Python you can adjust the drop down width manually, but I can't seem to find any reference to either of these options in PyQt. If I'm missing it in the documentation, a friendly pointer to the right spot would be of great help. Thanks.
The dropdown portion is a component that inherits QAbstractItemView (usually QListView).
It is accessible via the view() getter of the QComboBox. You can play with its sizePolicy to get what you want.
If it is not satisfying, you can even set your own QAbstraItemView to the QComboBox via `setView'.
In Qt a widgets size is usually controlled by its layout. The layout management docs should help. In particular, take a look at QSizePolicy. If you're using designer, you can set these directly as properties, otherwise you'll need to do it in code. The basic layouts example may help as well.
What would be a good approach to display and edit large amount of unformatted text (just like notepade does) using WPF? Loading a big string into a TextBox makes the UI unresponsive. The overall performance is not nearly comparable with TextBox Controls of previous Microsoft UI Frameworks.
What options do I have to solve this problem. I do not want to block the UI thread while the text control loads the text. Also I might need some sort of "virtualization" because it might not be a good idea to load the whole text into the control (I guess that 20MB of text would create a lot of glyphs even if they are not visible). It seems that TextBox doesn't even have an AppenText() Method anymore so I don't even have a way to control asynchronous loading of the text.
Isn't this a common problem? It seems that WPF does not provide anything for this out of the box. Why is this so?
AvalonEdit, the text editor in SharpDevelop, was written completely from scratch in WPF and is optimized for large amounts of text. It doesn't support rich text (although it does support syntax highlighting and other cool features such as folding). I think this might fit your bill perfectly.
Here is an article on the editor written by the developer:
http://www.codeproject.com/KB/edit/AvalonEdit.aspx
I am not sure if this helps, but have you tried using FlowDocumentPageViewer and FlowDocumentReader?
It also has very good annotations support and looks ideal for loading documents in text format.
The problem is that the TextBox is a single container element. List controls, such as ListBox virtualize very well because of container recycling. There really isn't anything simple that you can do to speed up the TextBox.
But the TextBox control does have an AppendText() method:
TextBox tb = new TextBox();
tb.AppendText("Hello");
So yes, you can use this to dynamicly add some text just like you mentioned.
You can just use a textbox with a style that gives the user more room to view the text. There are probably more advanced controls from Telerik and others but if you don't require editing options that should suffice.
You could always mix and match technologies: you could drop a WinForms TextBox onto a WPF parent. You lose things like styling, opacity, animation, transforms, etc., but if all that matters is editing text, the WinForms TextBox does that just fine.
Have you tried the WPF RichTextBox? You'll definitely want to read up on the FlowDocument information if you go this route.
You could use FlowDocument, but this doesn't work out of the box to bind to the Document property of a FlowDocument in MVVM.
Another solution is using FlowDocumentScrollViewer and bind to its Document property.
(or you could even use a FlowDocumentReader and bind its Document property, similar to the FlowDocumentScrollViewer. This gives you a different UI.)
The View:
<FlowDocumentScrollViewer Document="{Binding FlowDocument, Mode=OneWay}" />
The ViewModel:
FlowDocument fd = new FlowDocument();
Paragraph p = new Paragraph();
Run r = new Run();
r.Text = "large text";
p.Inlines.Add(r);
fd.Blocks.Add(p);
FlowDocument = fd;
private FlowDocument _FlowDocument;
public FlowDocument FlowDocument
{
get{ return _FlowDocument; }
set
{
_FlowDocument = value;
NotifyOfPropertyChange(nameof(FlowDocument));
}
}
see also this for extra performance tips: https://learn.microsoft.com/en-us/dotnet/framework/wpf/advanced/optimizing-performance-text#flowdocument-textblock-and-label-controls
How about trying something like this:
Keep the whole string in memory but show only a 'slice' of it in the textbox. Size of the that sliced string would be dynamically calculated depending on the size of textbox, font size etc.
Of course this involves a lot of not trivial code for proper displaying, synchronizing and so on, but it seems the way to go.
One other option is to use Scintilla.NET. It has the best performance for large amounts of texts I have seen so far. Loading large files is almost instantaneous. Even though it is a WinForms control, you can embed it into your WPF window with WindowsFormsHost container. There is syntax highlighting and some other features that should be more than enough for displaying unformatted text. From the downsides - since this is a WinForms control, it overlaps other WPF UI elements. Also there may be issues with resizing the control. Unfortunately, I don't see any better option than using a non-WPF control instead of the default TextBox and RichTextBox.