Retrieve the Toolbox Image associated with standard WPF controls - c#

I have a slim'd down IDE inside my application to allow some basic customization of a UI page. My toolbox contains some standard WPF controls. I am however having a hard time getting the Icon associated with any of the default WPF controls, I assumed they would have been defined by ToolboxBitmapAttribute just like user controls, but I am not able to see any such attribute on the types.
The following alwayse returns null on "default" WPF types such as Grid, Image, Border etc...
public Image Icon
{
get
{
Image controlImage = null;
AttributeCollection attrCol =
TypeDescriptor.GetAttributes(Type);
ToolboxBitmapAttribute imageAttr = (ToolboxBitmapAttribute)
attrCol[typeof(ToolboxBitmapAttribute)];
if (imageAttr != null)
{
controlImage = imageAttr.GetImage(Type);
}
return controlImage;
}
}

Related

User Control: how to add background image on button in setter/getter

In user control designer I know how to add background image.
My image (16x16px) is located in user control. How to add this image in code?
How do I embed the image in user control so that when I bring the control to another project, the image also appears?
private Bitmap buttonResetImage = null;// <-- here I want to load my initial image.Then the user can change this image as he wishes;
public Bitmap ButtonResetImage
{
get { return buttonResetImage; }
set
{
buttonResetImage = value;
btnReset.BackgroundImage = buttonResetImage;
btnReset.BackgroundImageLayout = ImageLayout.Center;
}
}
Well since MDP does not respond I found the trick. I need to initialize this:
private Bitmap buttonResetImage = global::SliderControl.Properties.Resources.Reset;
If I use only:
private Bitmap buttonResetImage = SliderControl.Properties.Resources.Reset;
I get this error:
Error CS0117 'SliderControl' does not contain a definition for 'Properties' SliderControl
https://i.imgur.com/az52MOP.png
In order to create a user control that has any kind of resources like :
images
fonts
audio
icon
and etc, you can create them in a separate project (called Windows Forms Control Library) and add resources in it.
after all, the result(in this case, a dll file) contains all the resources and code base you need.you can use it in any where you want.

How to refactor duplicated code in Windows Forms?

I'm currently working on refactoring a lot of duplicated code in a couple of UserControls in Windows Forms project.
The architecture leaves much to be desired with a lot of logic implemented inside the UI layer. This will be fixed eventually, I'm now working on improving what I have on my hands now.
The problem is, that a lot of duplicated code relates directly to controls, for instance:
private void InitDestinationPathControls(string path)
{
if (someField)
{
tbOne.Enabled = false;
tbOne.Visible = false;
btnTwo.Enabled = false;
btnTwo.Visible = false;
tbOne.Text = string.Empty;
return;
}
// (...)
}
Don't get too attached to the cited code itself, it is just an example.
I'd like to move this code to a common base class, but it relies directly on specific fields (even though they are exactly the same in all controls too). Those fields, on the other hand, are generated by the designer, so I cannot extract them to the base class.
The only thing that comes to my mind is to pass those fields as parameters to a method in base class, but then if some method uses a lot of them, I'll end up with a monstrous interface part and that won't actually improve the readability too much.
How can I deal with such common parts of user controls in Windows Forms?
Apparently you have a combination of several controls that appears in several forms. In other words: you have for instance some buttons, comboboxes, etc, that you want to show on different forms, and you want them to have the same behaviour.
If not only the behaviour of these controls is the same on all forms, but also the layout, then consider to create a class derived from UserControl.
The UserControl hides from the outside world which controls are shown, how they are visualized and how they behave.
I assume that you already pulled the model out of the visualisation of the data.
If all instances of this user control should all call the same functions of possibly a different object of the same class, then give your special user control a property that represents this class, or at least an interface. During construction you can plug in the actual class that should handle the actions after operator input.
If, on the other hand, the layout differs on each form, but the collection of buttons, comboboxes, etc and their behaviour is similar on all forms that show this collection of controls and they have a lot of common behaviour, consider to create your own ControlCollection.
For instance, if on several forms you have a button to select a (text) file, labels with the name, size and creation date of the selected file, and an edit box that shows the content of the text file, but you want to layout them differently, consider something like this:
class FileDisplayControls : IDisposable
{
public Button ButtonSelectFile {get;} = new Button();
public Label labelFileName {get; } = new Label();
public Label labelFileSize {get; } = new Label();
public TextBox textFileContents {get; } = new FileContents();
private void ButtonSelectFile_Clicked(object sender, ...)
{
// TODO: open file dialog, display result in labels and text box
}
}
Constructor can set initial layout properties of the controls, and subscribe to events, such that the controls will react on user input.
The user of the class (= code, not operator) immediately has a collection of controls that have some standard behaviour, like react on button click. All he has to do is set the location of the items in his own form. If desired change other layout properties (colour, background) and put them on his own form.
If you want to prevent that others change other visual aspects of the controls than the position, don't publish the control themselves, only the position of the control:
public System.Drawing.Point LocationSelectFileButton
{
get => this.buttonSelectFile.Location;
set => this.buttonSelectFile.Location = value;
}
public System.Drawing.Point LocationFileContentTextBox
{
get => this.textBoxFileContent.Location;
set => this.textBoxFileContent.Location = value;
}
etc.
If needed, you can add events for users:
public event EventHandler SelectedFileChanged;
public string FileName => this.labelFileName.Text;
public string FileContents => this.textBoxFileContent.Text;
etc.
Conclusion
The solution that you choose depends on the similarity between the controls on the various forms:
if Behaviour and Layout are all the same: UserControl
If only position and a few properties different: special class with the properties that are different. This way you can force a more similar style: all "Select File" buttons look similar.
If only one or two behaviours are different: add Action<...> properties or events
If you want full control of the layout: expose the Controls.
The behaviour that is common for all you forms that show these controls (in my example: how to select a file and what to do when a file is selected) is inside the class.
repeated code can be extracted to method (possibly in base class, or as static method in helper class)
public void DisableControls(params Control[] controls)
{
foreach(var c in Controls)
{
c.Enabled = false;
c.Visible = false;
if (c is TextBox t)
{
t.Text = string.Empty;
}
}
}
private void InitDestinationPathControls(string path)
{
if (someField)
{
DisableControls(tbOne, btnTwo);
return;
}
// (...)
}

Controls inside custom control aren't movable

I have a custom control (let's say MyContainer) that simply is a ScrollViewer with a Canvas inside.
I'm able to add controls to MyContainer like in a Canvas but in XAML designer this controls aren't movable like in a normal Canvas; they can't be moved with the mouse.
Here's the MyContainer code:
[ContentProperty("Children")]
public class MyContainer : ScrollViewer, IAddChild
{
private Canvas _innerCanvas;
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public UIElementCollection Children
{
get { return _innerCanvas.Children; }
}
public MyContainer()
{
this._innerCanvas = new Canvas();
this.Content = _innerCanvas;
this.Loaded += MyContainer_Loaded;
}
void MyContainer_Loaded(object sender, RoutedEventArgs e)
{
_innerCanvas.Height = this.ActualHeight;
_innerCanvas.Width = this.ActualWidth;
}
void IAddChild.AddChild(object value)
{
if (value == null)
{
throw new ArgumentNullException("value");
}
UIElement uie = value as UIElement;
if (uie == null)
{
throw new ArgumentNullException("value");
}
_innerCanvas.Children.Add(value as UIElement);
}
void IAddChild.AddText(string text)
{
;
}
}
Where am I wrong?
PS: please avoid replies like "don't use editor, use XAML code only"; I need to make a User Control usable via graphic interface.
I think that you did not choose the correct base class. In WPF there are certain extensibility points that you should use for certain types of UI elements and I guess that the designer is hard-wired to these classes.
The different types of UI Elements are:
Visuals: they usually derive from FrameworkElement and their purpose is to display something that the user normally does not interact with (e.g. a text block).
Controls: they represent something that the user can interact with, like buttons, check boxes, text boxes, scroll viewers, etc. They usually derive from Control or ContentControl.
Panels: their purpose is to layout other UI Elements, Grid or StackPanel are examples. They all derive from the Panel base class.
Items controls: they usually provide selection for a number of items. ListBox, ComboBox, and TreeView are examples for them. All of them derive from ItemsControl.
Another important thing is that ContentControls and ItemsControls can display any object, not only those that can render themselves. They use the WPF Data Templating mechanism for that (the default is calling ToString on a non-renderable object and putting the resulting string in a TextBlock).
According to your code, I would assume that you either want to implement a panel or an items control. For panels, you should know about the Measure - Arrange - Render cycle of WPF and how you can use it to layout the panel's children.
Implementing an items control is a little bit harder because essentially an items control uses items that wrap the actual content of each displayed object (e.g. ListBoxItem), a panel to layout these items, an items container generator to dynamically create the child items, and of course you can use styles and templates. Most of the items controls also incorporate a scroll viewer. If you want to learn more about items controls, I strongly encourage you to read the "Items Controls: A to Z" blog series by Dr. WPF.
I haven't tried it out but I'm sure if you choose the correct base class to extend from, then you can use your control with the designer properly.

C# & XAML Popup doesn't show in screenshot

I have many popups in a custom GUI application. These popups are window objects, not popup objects. The popups do not show up in a screenshot when using the Print Screen button on the keyboard. Instead, the disabled mainwindow below is all that shows in the screenshot. The popup never flickers or disappears, it just doesn't show in the screenshot.
WindowInstance.IsEnabled = true;
WindowInstance.Refresh();
DisplayPopUpWindow(WindowInstance);
The code in DisplayPopupWindow:
private void DisplayPopUpWindow(Window theWindow)
{
if (theWindow != null)
{
if (theWindow is MessagePopup)
{
// Only show one popup at a time (queue is handled elsewhere)
RemovePopUpsCommand.Execute(true, null);
}
ActiveWindow.IsEnabled = false; // main screen disabled
foreach (KeyValuePair<Window, int> aPopup in popUpWindows)
{
if ((aPopup.Key.IsVisible) && (aPopup.Key.Opacity == 1) && (aPopup.Key != theWindow))
{
// if window is a lower priority then disable it
if (aPopup.Value > displayPriority)
aPopup.Key.IsEnabled = false;
}
}
theWindow.Show();
theWindow.Opacity = 1;
}
}
Is there some property in the XAML that affects whether the window is visible for screenshots? This is a large issue as this also affects some remoting software we use in that popups do not display on the shared screen. Also affects our combobox implementation.
The "popups" are actually their own standalone windows. Some have instances created once during application startup and simply shown/hidden when needed, however, most are created on demand to be displayed. This problem affects both types.
The remoting software used is axeda access remote.
If I remember correctly I had the same problem and I think it was related to setting the popup windows parent to the main window that fixed it, I'd have to look at my code at home to confirm.
So make sure this is correctly set.
EDIT:
Try using this code when you create the Window object:
MainWindowVariable.Owner = Window.GetWindow(this)
You can read more here:
http://msdn.microsoft.com/en-us/library/system.windows.window.owner(v=vs.110).aspx

Winform Custom Control: Why Setter not called when used from Constructor?

I have an initial value property like this:
[Category("Main")]
[Description("Intial Value")]
[DefaultValue(10)]
public int InitialValue
{
get { return m_initialValue; }
set {
m_initialValue = value;
this.TrackBar.Value = this.m_initialValue;
}
}
So in my constructor I do this for example:
this.InitialValue = 10;
To my surprise when dragging the custom control on a form the setter is not called so that my trackbar value is not synchronized.
Why ?
Only when I change the property in dialog box the setter is called.
I decided to take your advice as suggested in one of the comments:
You can try by yourself will take 2 minutes.
So I did (it took about 3 minutes), and I was unable to reproduce the behavior that you described.
Here are the exact steps that I followed:
Created a new Windows Forms Application.
Added a new User Control to my project.
Opened the new User Control in design view and added a TrackBar control (leaving the TrackBar control's properties all set to their defaults).
Added the following code to the User Control class (exactly the same as you posted above, with the addition of a private field m_initialValue that you omitted from the original example):
public class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
this.InitialValue = 10;
}
[Category("Main")]
[Description("Intial Value")]
[DefaultValue(10)]
public int InitialValue
{
get { return m_initialValue; }
set
{
m_initialValue = value;
this.trackBar1.Value = this.m_initialValue;
}
}
int m_initialValue;
}
Built the project.
Opened the default Form (Form1) that was created with the new project in design view.
Dragged the User Control that I had just created (UserControl1) out of the toolbox where it was automatically placed and onto the surface of the form.
The indicator on the slider bar appeared all the way to the right side (the correct and expected position given the default Maximum value of 10). Now, you tell me: What are we doing differently?
Try adding [Browsable(true)] .
The key portion of your question is here:
when dragging the custom control on a form
You're still in the designer, and the designer cheats a bit to render things. Does this still happen when you actually run the application?

Categories