I have two controls. The XAML's are big and very similar. One difference is this: they contain a listbox, in one control, it's bound to {StaticResource X}, and is multiselect, the other is bound to {StaticResource Y}, and is not multiselect. The code-behinds are also very similar. How should I combine these two classes into one? I thought about creating a base class and deriving my 2 controls from it, but I have no idea how to do that with XAML. I know I could make it easier if I set the differing properties in code instead of XAML (in which case the XAML's would become identical), but let's consider that plan B. Silverlight has no StyleSelector, it seemed like a possible solution though. Maybe VisualStateManager could do it, except it sounds bad, because my problem has nothing to do with visuals, but maybe I could define 2 states anyway. Except I think SL doesn't support binding in style definitions. Tough question for a beginner like me...
You should look into creating custom controls and using AlternateContent properties. Look these up and you'll find hundreds of tutorials.
Here's a quick google search to get you started with alternate content.
So, to sum it up, I want one control which can work in somewhat different modes, or states. The mode can affect XAML properties and code logic, too.
It seems like VisualStateManager is very limited in which properties it can manipulate. But when the differences are only visual, it's the best choice.
When there are other differences in XAML, then the obvious choice is to omit those properties from XAML and set them in code, like in the ctor. A nicer way is to expose those properties as dependency properties in code, bind to those properties in the XAML of the user control, and then you can specify those properties in other XAML's where you use this user control. When your control doesn't care what's in those properties, then it's a good design choice, too. In my case, though, when setting up those differing properties should be the responsibility of the user control itself, not its parent, and I want to expose a single mode property only, it's not good.
For this case, the best way I found so far is this:
create a normal user control (XAML+code), expose the differing properties (simple, not DP's) and bind to them in XAML
make this user control abstract, and possibly some properties, too
for each different mode the control needs to support, derive a class from this base control (code only, no XAML), provide implementations for the abstract properties
instead of using the base control in other places, use one of the derived implementations
This way, you can easily specify from outside which mode you want your control to work in. The drawback is that it's not easy to change the mode, since it's not a property you need to change but the type and instance of the control.
And finally, when there are code logic differences, too, then one way is exposing a mode property, or using the abstract class method I described above. For example, a button click handler function can be abstract, too.
Related
Are the names for states and VisualStateGroups in VisualStateManager arbitrary, or do they have deeper meaning?
They seem similar to pseudo-classes in CSS, in some ways (Button has CommonStates: Normal, PointerOver, Pressed, and Disabled, whereas CSS has :hover, :active, and ways of targeting disabled controls). But they are also different—Checkbox has (it seems) completely different states, like Checked, Unchecked, and Indeterminate.
As I develop my apps, how do I know which VisualStates are available? What happens if I use the wrong names?
Disclaimer: I work for Microsoft.
Do the names for states hold deeper meaning?
Are the names for states and VisualStateGroups in VisualStateManager arbitrary, or do they have deeper meaning?
The names for states and VisualStateGroups are indeed arbitrary, but they do have a deeper meaning.
VisualStates in VisualStateManager are [representations of] the visual appearance of a UI element when it is in a specific state. They use Storyboards or Setters to represent arbitrary changes to a given Control (for example, make the background red). Control authors can call VisualStateManager.GoToState within their codebehind to activate a named VisualState.
In this way, VisualState names are arbitrary: I could create a VisualState named CitelaosGreatState and activate it by calling GoToState(this, "CitelaosGreatState"). That's why there's such a variety of names—Buttons have the CommonStates you mentioned, while Checkboxes have completely different ones.
However, for practical purposes, the VisualState names you choose are not arbitrary.
If you are using a VisualStateManager to modify the look & feel of a default control (by overriding the ControlTemplate), you almost certainly want to pick VisualState and VisualStateGroup names that correspond exactly to the names in the original ControlTemplate (which you can find in the XAML designer or in generic.xaml). This is a matter of functionality: the implementers of, say, Button chose an arbitrary name for the VisualState that appears when you hover a Button—CommonStates PointerOver. The logic in their codebehind that handles hovering a Button calls GoToState(this, "PointerOver") (or some equivalent). If you want to customize that appearance, you have to provide a VisualState with that name.
You can certainly also provide a cool state named CitelaosGreatState, or even Hover, but if the codebehind never calls GoToState with that same string, your state will never be activated, no matter the name.
Of course, that means for completely custom controls (where you control both the VisualStateManager and calls to VisualStateManager.GoToState), you can name your states whatever you want!
How do I know which names to choose?
As I develop my apps, how do I know which VisualStates are available?
Completely custom controls: as above, if you are writing both the VisualStateManager and the calls to VisualStateManager.GoToState, name the VisualStates whatever you want! If you define a state named State1, you can activate it in codebehind with GoToState State1.
Custom templates for existing controls: if you are retemplating an existing control (using ControlTemplate), you should choose names that match the existing states on that control. For example, Button defines CommonStates: Hovered, PointerOver, Pressed, and Disabled. Define those. States with other names will not ever be activated (since the codebehind written by the XAML team doesn't ever GoToState with that name). To know the existing states—
How do I know the existing VisualStates of a control?
Look up the default ControlTemplate (or Style with TargetType of your desired type) for your control (which, as mentioned earlier, you can find in the XAML designer or in generic.xaml).
Use the names in the template's VisualStateGroup.
Wrong name?
What happens if I use the wrong names?
Since the names are arbitrary, "wrong name" is not the clearest question here. It's easier to look at two cases:
You define a VisualState that codebehind never activates with GoToState (e.g. you added a state called CitelaosGreatState or Hover to your custom template for a Button): nothing awful happens, but your state will never be activated.
You fail to define a VisualState that codebehind tries to activate (e.g. you added an incomplete VisualStateGroup for your custom Button template): the GoToState call has no effect.
To illustrate what I'm asking, let's say I have a custom TextBox that has 2 modes. Mode 1 only allows numbers, and mode 2 only allows dates in a particular format.
Using a class module I can create this custom TextBox, and I can use a loop when the userform initialises to set which TextBoxes are custom.
What I'd like to happen is have the custom TextBox, or what ever custom control I want, show up in the toolbox. And I also want its custom properties, if they exist, to show up in the property window.
So far, I've been unable to find a way to do this. In fact, I've been unable to find out if it's even possible. It seems, to me anyway, that it's something that should be possible, but maybe I'm barking up the wrong tree. If it's possible I'd really appreciate being pointed to a resource.
Setup:
I have created a Form that I wish to have serve as the base from which I will inherit other forms. This base form serves as a "template" of sorts, but it also provides a good deal of functionality related to the structure, as well as the interrelation of all of the controls provided.
A primer for the images that follow... The top info-colored bar is a custom control inherited from ToolStrip. The bottom strip is another custom, again inherited from ToolStrip. The left white block is a TreeView and the right block is a TabControl (having deleted all TabPages from it...I intend for these to be added in the inherited forms).
Image of base form in designer:
Image of inherited form in designer:
Clearly, the only difference is that when I open the inherited form, I get a little box icon superimposed over each control, and when I click them, I get the padlock telling me I cannot edit.
The problems:
All controls on the inherited form are locked. I have researched the issue of visual inheritance, and as far as I can tell, I'm not using any controls that expressly do not support it, as this link suggests there are. In this Q&A, Hans suggests changing the modifier on those controls, which I have done. In fact, I tried both Public and Protected, all to no good result.
I am stumped.
This is a technical restriction in the designer, it is specific to the SplitContainer control you are using. And some other ones. The trouble-maker is the ISupportInitialize interface.
Controls use this interface when they can't afford the properties of the control to be assigned in an arbitrary order. The designer helps when it sees that the control implements this interface, it calls the BeginInit() method when it starts assigning properties, EndInit() when it is done. The control uses these methods to delay the side-effect of property assignments, the EndInit() method makes them effective. Important for SplitContainer, the minimum sizes of the panels also affect the splitter position.
Perhaps you can see the rub, the InitializeComponent() method in the base form class has already called ISupportInitialize.EndInit(). So modifying properties again in the derived form class is unlikely to turn out well. The designer protects the control from this by locking it.
Very inconvenient, there is no simple workaround. If modifying the SplitContainer in the derived form class is a hard requirement then you'll have to give up on inheriting it or write the code by hand in the derived class constructor.
I'm a rookie at VBA(Excel) but I know some OOP stuff from java. What I want to do is extend the class MSForms.TextBox to add my own constructors and properties. I've tried using the implements keyword but my class won't compile. It would also be helpful to add these fields into some kind of container variable since they're arranged in a 9x9 pattern. Here's what I want my custom textbox to do. (It's for a game)
I need to be initiate it and place it on the form and put initial text into the box based on what is passed to the constructor. I also would like to have it check the values at runtime with an event to make sure they comply with the game rules. If it does not comply I would like to change the background color to red.
I'm able to do all of this when I just make a bunch of form controls the old fashioned way and use a sub controlled by a button to check the text box values.
Any help on extending classes would be much appreciated.
Thanks
I don't think you can do that in VBA. I would keep two arrays, one array of TextBox and one array of a new class representing your extended properties. Then you keep them in sync. e.g. Textbox #71 and extended properties #71.
You can look at the following link for making and working with control arrays.
http://www.siddharthrout.com/index.php/2018/01/15/vba-control-arrays/
I have some conditionals in my InitializeComponent which affect the layout based on some variables. Unfortunately, it seems like whenever I rebuild my application, this code is reverted back to its previous state. Is this code being regenerated based on the Designer interface? Is there a way to prevent it from doing this?
Yes, InitializeComponent is completely IDE-generated; don't even try to mess with it.
If you have conditional logic wherein you want to add/remove some controls, do it in your control's constructor after the auto-generated call to InitializeComponent.
Note that if the conditional stuff will depend on features enabled/disabled at design time (e.g., if someone else is using your control and you've provided properties to affect how that control behaves which you intend to be set at design time), using the constructor won't work since the constructor will have already run by the time the user makes his/her choices from the design view; in this case, override the OnLoad method and put your logic in there (or do some variation of this, e.g., handle the Load event itself—there are plenty of ways to skin this cat).