Call custom action in WIX on change of value in Combox - combobox

Am stuck with combo box and custom action in WIX installer.
I have a combo box(drop down) containing few values. I want to show some text on the screen (unique for each item in dropdown) when the user selects a value from this drop down.
In .Net we can do this easily as we have different events pre-defined. But in WIX I don't see any such event.
Has someone faced the same problem? Or can guide me on how can I get it done.

Windows Installer (the underlying technology) doesn't let you do so. Literally, it doesn't publish any event when the combobox (dropdown) value changes. You'll have to add a button, for instance, for user to click when he/she changed the value in the combobox...
Alternatively, you can switch to the EmbeddedUI technique (WiX element and MSI table), but it is much more advanced...
UPDATE: a sample of using button click to update the text.
<UI>
...
<ComboBox Property="WIX_VERSIONS">
<ListItem Value="Windows Installer XML 3.0" />
<ListItem Value="Windows Installer XML 3.5" />
<ListItem Value="Windows Installer XML 3.6" />
</ComboBox>
...
<Dialog Id="MyCustomDlg">
...
<Control Id="ComboBoxMain" Type="ComboBox" X="10" Y="60" Width="300" Height="17" Property="WIX_VERSIONS" />
<Control Id="ButtonMain" Type="PushButton" X="320" Y="60" Width="40" Height="17" Text="Show">
<Publish Property="COMBOVALUEFORMATTED" Value="You've chosen the [WIX_VERSIONS] version of the toolset" />
</Control>
<Control Id="LabelMain" Type="Text" X="10" Y="80" Width="360" Height="17" Property="COMBOVALUEFORMATTED" Text="[COMBOVALUEFORMATTED]" />
...
</Dialog>
</UI>
PushButton can publish more events, for instance, DoAction, which is used to run a custom action on button click. This might be more relevant in your case.

There is a way to do this in WiX. You just need to manufacture your own changed event.
We compare our DoAction condition to another property which will hold the previous state of the Combobox - VIRTUALWEBSITEOLD
Execute a custom action in the ComboBox when old does not equal new:
<Control Id="WebSite" Type="ComboBox" Width="180" Height="18" X="120" Y="48" ComboList="no" Property="VIRTUALWEBSITE">
<Publish Event="DoAction" Value="LansaInitVirtualFolders"><![CDATA[VIRTUALWEBSITE <> VIRTUALWEBSITEOLD]]></Publish>
</Control>
Then the Custom Action performs the same comparison as the DoAction (probably not required) and then saves the Combobox value in the OLD property.
Tstring wszWebsite = ReadProperty( _T( "VIRTUALWEBSITE") );
Tstring wszWebsiteOld = ReadProperty( _T ( "VIRTUALWEBSITEOLD" ) );
// If unchanged ignore request
if ( wszWebsite == wszWebsiteOld ) return true ;
[Do Some stuff]
// Set the saved state of the combobox so we don't get called again until it changes
if ( nResult == ERROR_SUCCESS || nResult == ERROR_NO_MORE_ITEMS)
{
WriteProperty( _T("VIRTUALWEBSITEOLD" ), wszWebsite.c_str () );
}
(Note: Also need to use Twin Dialog Pattern if updating, say, a listbox control. If your control doesn't update but Next and Back DOES update it, then Twin Dialog Pattern will ensure it updates)

Related

PropertyRef to require ComboBox

In one of my dialogs, I have the following control:
<Control Id="EnvironmentComboBox" Type="ComboBox" Sorted="yes" ComboList="yes" Property="ENVIRONMENT" X="25" Y="110" Width="200" Height="15" />
I fill the ComboBox in elsewhere like so:
<UI>
<ComboBox Property="ENVIRONMENT">
<ListItem Text="Development" Value="Development" />
<ListItem Text="SIT" Value="SIT" />
<ListItem Text="UAT" Value="UAT" />
<ListItem Text="Production" Value="Production" />
</ComboBox>
</UI>
However, if I don't have the ComboBox bit created, the MSI will still build, and it will fail during install (2205). Thus, I would like to enforce the requirement to have a property named ENVIRONMENT. I've tried adding a PropertyRef like below to my dialog:
<PropertyRef Id="ENVIRONMENT" />
However, this doesn't seem to pick up the <ComboBox Proeprty="ENVIRONMENT">. It will pick up a regular property (<Property Id="ENVIRONMENT" Value="test" />), but that doesn't really help much.
Is there any way to require a ComboBox to be defined?
EDIT: For clarification, I intend to keep the ComboBox definition separate from the Control definition so that the dialog can be reused.
MSI can exist without Combobox items declared for the property, referenced by a element (you have the ability to type any text you need in the combobox text field in case ComboList='no').
*The element doesn't have any corresponding object in the MSI tables* (only it's child elements gets written to the MSI's ComboBox table). So I suppose this element is completely optional from WIX point.
Your error #2205 is caused by non-existance of the 'ComboBox' table at all. I suppose you have only one combobox in the installer. Theoretically it is impossible possible to detect this at compile-time as error (tables can be created in custom actions as well). The most WIX team can do is generate warning.
To avoid this error I declared a dummy combobox element in my reusable project:
<Fragment><!--This fragment is intended to fill MSI tables with dummy items so that these tables became created. Tables without items aren't created-->
<UI Id="Dummy">
<Dialog Id="DummyDlg" Width="370" Height="270" Title="Dummy" NoMinimize="yes">
<Control Id="DummyDlgComboBox" Type="ComboBox" Property="DummyComboboxProperty" Width="200" Height="17" X="100" Y="80">
<ComboBox Property="DummyComboboxProperty">
<ListItem Text="Dummy" Value="Dummy" />
</ComboBox>
</Control>
</Dialog>
</UI>
</Fragment>
and referenced this UI from my commonly-used UI sequences.
Now I don't need to worry about existance of the combobox table in any of my installers.
As for a workaround to your problem - how to enforce users not to forget to declare list items, I would do a tepmlate wix file instead of the wixlib. Something like this:
<Control Id="EnvironmentComboBox" Type="ComboBox" Sorted="yes" ComboList="yes" Property="ENVIRONMENT" X="25" Y="110" Width="200" Height="15">
<ComboBox Property="ENVIRONMENT">
<Placeholder Id="EnvironmentComboBoxItems" />
</ComboBox>
</Combobox>
and give users the only ability to reuse this code using template transformation tool you provide. It will validate if all template placeholders are provided with content.
The transformation may look like this:
<TemplateSubstitutions>
<PlaceholderContent PlaceholderId='EnvironmentComboBoxItems'>
<ListItem Text="Development" Value="Development" />
<ListItem Text="SIT" Value="SIT" />
<ListItem Text="UAT" Value="UAT" />
<ListItem Text="Production" Value="Production" />
</PlaceholderContent>
</TemplateSubstitutions>
The tool for merging them:
static void Main(string[] args)
{
var templatePath = args[0];
var templateTransformPath = args[1];
var resultPath = args[2];
var templateDoc = XDocument.Load(templatePath);
var transformationDoc = XDocument.Parse(templateTransformPath);
Dictionary<string, XElement> contents = transformationDoc.Element("TemplateSubstitutions").Elements("PlaceholderContent").ToDictionary(e => e.Attribute("PlaceholderId").Value, e => e);
var planceHolders = templateDoc.Descendants("Placeholder").ToArray();
foreach (var ph in planceHolders)
{
ph.ReplaceWith(new XElement(contents[ph.Attribute("Id").Value]).Nodes());
}
templateDoc.Save(resultPath);
}
Of course this tool isn't release yet - you may want to add some meaningful error messages to your clients and validation of provided transformations. But to avoid code complication I didn't implement that.
I use this approach in few installer projects I have currently. All templates are stored in the common location and client installers can take any file they need and transform it.
My release tool is a bit more advanced of course. It can substitute values in the attributes using wildcards - I consider it extreemely useful when reusing components. I use then template like this:
<Component Guid="{StrToGuid({ProductName}_7A51C3FD-CBE9-4EB1-8739-A8F45D46DCF5)}">
and user should provide all template properties (such as 'ProductName') in command-line of my template transformation tool. It ensures components will have unique GUIDs between different products, so they will not conflict when installed on the same machine.
NOTE: Be careful with figure brackets though - they have special meaning for WIX and MSI: http://msdn.microsoft.com/library/aa368609.aspx. But as for me it is not clear enough and I don't use them regularly. But you might need another wildcard prefix.
As for organizing the installer build process, you can add template transformation calls on pre-build of the project. But I decided to use separate build scripts instead of native Visual studio build. It looks much more simple and flexible. And I don't get nasty errors like "command 'xxx' exited with code yyy" - I always see the template transformation log, error messages etc.
Hope my reinvention of the wheel will help anybody =).
I suspect <PropertyRef> was designed the way to pick up only the "direct" definitions of properties, that is, <Property> elements. The <ComboBox> just mentions the property name in its attribute, and this is not treated as a property definition.
Add a "direct" property definition to your sample, and it should work:
<UI>
<Property Name="ENVIRONMENT" Value="" />
<ComboBox Property="ENVIRONMENT">
<ListItem Text="Development" Value="Development" />
<ListItem Text="SIT" Value="SIT" />
<ListItem Text="UAT" Value="UAT" />
<ListItem Text="Production" Value="Production" />
</ComboBox>
</UI>
And reference it with <PropertyRef> element in another place - just the way you tried.
As far as I know, such a definition won't harm the combobox part, and you'll be on the safe side with proper fragment inclusion.
Alternatively, you can reference the entire <UI> element with <UIRef> element - it should have the same effect.
Why not define your combobox inside the element? Like this:
<Control Id="EnvironmentComboBox" Type="ComboBox" Sorted="yes" ComboList="yes" Property="ENVIRONMENT" X="25" Y="110" Width="200" Height="15">
<ComboBox Property="ENVIRONMENT">
<ListItem Text="Development" Value="Development" />
<ListItem Text="SIT" Value="SIT" />
<ListItem Text="UAT" Value="UAT" />
<ListItem Text="Production" Value="Production" />
</ComboBox>
</Combobox>
In this case you won't be able to reference dialog not referencing the combobox. Maybe I understood you wrong, but it looks logically to avoid user errors rather than throwing them.
If your user needs to be able to change list items of your library-dialog, you can populate dialog items with custom action for example (though it is probably not the best approach in case of static values).
I also faced plenty of problems with reusing WIX elements and ended up with creating own template project and using some transformation of wxs files (for now quite simple, but can implement anything I need) for every installer I create. Works great and gives infinite flexibility.

Storing the value of CheckBox in WiX

I have authored an installer using Wix 3.6 RC. I have a checkbox in the dialog UI which is disabled and unchecked initially. There is a "Verify" button next to it. When I press this button a custom action gets executed (immediate) and sets a value of parameter which decides whether check box gets enabled or not. Here is the code:
<Control Id="VirtualCheckBox"
Type="CheckBox"
CheckBoxValue="1"
X="35" Y="100"
Width="160" Height="20"
Disabled="yes"
Text="!(loc.VirtualizationDlg_ChkBox)" Property="ENABLEVIRTUALIZATION">
<Condition Action="enable"><![CDATA[INTEGRATED = "1"]]></Condition>
<Condition Action="disable"><![CDATA[INTEGRATED = "0"]]></Condition>
</Control>
I am storing this value of checkbox in registry:
<Component Id="Virtualization_RegistryEntries" Guid="GUID-IS-HERE">
<Condition>Not Installed</Condition>
<RegistryKey Root="HKCU"
Key="Software\!(loc.CompanyName)\!(loc.ProductName)">
<RegistryValue Type="string" Name="Virtualization" Value="[ENABLEVIRTUALIZATION]" KeyPath="yes"/>
</RegistryKey>
</Component>
Now I have to do this:
1) If user does not click the Verify button : Registry gets value 0
2) If he clicks button (CA gets false and checkbox remains disabled) : Registry gets value 0
3) If he clicks button and checkbox enables but he does not check the checkbox : Registry gets value 0
4) If he clicks button and checkbox enables and he does check the check box : Registry gets value 1
Mine is not working at only one condition, when he checks this, registry does get 1 but either it is disabled or remains unchecked the registry gets nothing.
How can I resolve this issue?
Have you tried giving your variables a default value?
<Property Id="ENABLEVIRTUALIZATION" Value="0" />
Something as simple as that should do the trick!

login design in prism 4.0

this question might sound silly. I want to do login page by editing the template in VS2010 with Prism 4.0 as the template. as a login page, it will have 2 textbox, one is username and another one is password. in one button click, i want that button to retrieve both of the value. currently this not happen,because it just carry 1 value. i am new to this MVVM. if i put the code in the view part, perhaps this might violate the model.
this is part of the code (from the template)
<Button prism:Click.Command="{Binding Login}"
prism:Click.CommandParameter="{Binding Username}" Margin="2"
ToolTipService.ToolTip="Click to navigate to the Edit View for this item." IsCancel="True" IsDefault="False"><Image Height="20" Width="20" Source="/Module1;component/Images/NavigateToView.png" />
</Button>
You could create two new properties in your view model and bind your textboxes to them in xaml
<TextBox Text={Binding Username, Mode=TwoWay} />
<TextBox Text={Binding Password, Mode=TwoWay} />
Then, in your Login command implementation, you could simply use these properties, as they reflect the data the user has entered in the textboxes. This way you don't have to use a command parameter either.

How to thru char into TextBox?

I have some TextBox on focus ( cursor is blinking on him ).
Now, from other usercontrol ( that contain buttons ) i want to send event that will insert char into the TextBox when pressing any of the buttons that are on the usercontrol.
This need to be without lose the focus from the TextBox ... ( blinking cursor on the TextBox )
How can i do it ?
( i try to raise key down event - but its does not work )
Make your buttons to be not focusable (Focusable = false).
Do you want to use this virtual keyboard with other applications, or is it something that's only going on in your application? Beyond that, if it is only your application, do you only ever want to insert characters into one particular TextBox, or potentially any TextBox?
If it's a virtual keyboard intended to work with any application, then you'll want to use a Win32 API method like SendKeys. WinForms has an extremely easy interface for using SendKeys.
If it only ever needs to add characters to the one TextBox, then it's much more easy to modify the TextBox's Text property rather than trying to raise events on it to get the desired behavior. There's a CaretIndex property that will tell you where to insert the character. Beyond that, it's simple string concatenation.
<StackPanel>
<TextBox Name="MainTextBox" />
<Button Content="A"
Focusable="False"
Click="Button_Click" />
<Button Content="B"
Focusable="False"
Click="Button_Click" />
<Button Content="C"
Focusable="False"
Click="Button_Click" />
<Button Content="D"
Focusable="False"
Click="Button_Click" />
<Button Content="E"
Focusable="False"
Click="Button_Click" />
</StackPanel>
Code:
private void Button_Click(object sender, RoutedEventArgs e)
{
MainTextBox.Text += (sender as Button).Content.ToString();
}
var text = button.Content as string;
textbox.SelectedText = text;
textbox.SelectionLength = 0; // removing selection from inserted text
textbox.SelectionStart += text.Length;
This will insert button content at cursor position (and replace selected text) - the same as user inputted it from keyboard.
PS. if textbox is unknown, it may be found with
var textbox = FocusManager.GetFocusedElement(FocusManager.GetFocusScope(this)) as TextBox;
Instead of FocusManager.GetFocusScope(this) you may put window.
If you need it not only for textboxes - WinAPI functions should help. See http://www.pinvoke.net/default.aspx/user32.sendinput

Adding and changing usercontrol at run time in a WPF application

I have a situation as follows, i am going to use WPF for first time, so any suggestion abt how to proceed whould be great:
I hav a drop down, when i select any item from it - it should change the structure of controls in same window. New controls contain - two menu items and a text box and a list box. Selecting one menuitem will display text box and other will show list box. Now for each item in initial combo box i will have different info for the each menu items.
Problems:
Say i have 10 items in combo box - and 2 menu items for each - so 20 different stuff to show.
-- How should i declare these 20 different stuffs
-- How should i load each when a particular combination is selected
You should look at ControlTemplate. You can define a set of templates, then apply them to a control causing them to be whatever you want them to be. so, when the item changed event fires on your dropdown, load and apply the template that you want.
<!--- your xaml file --->
<Control x:Name="Main"/>
// you CS file....
OnItemChanage(....)
{
if ( Main!= null )
Main.Template = MyNewTemplate;
}
If you want to show multiple sets of controls at once, dd all of the controls to your window and set their Visibility using a data binding, and use the ComboBox to update the propertie the controls are bound to.
Or if you only want to show one control at a time, just use a DataContext from the ComboBox:
<Window.DataContext>
<x:Array x:Key="myItems">
<local:Item MenuItem1="abc" MenuItem2="def" />
<local:Item MenuItem1="ghi" MenuItem2="jkl" />
...
<local:Item MenuItem1="ghi" MenuItem2="jkl" />
</x:Array>
</Window.DataContext>
<Grid>
...
<ComboBox x:Name="selection" ItemsSource="{Binding}">
...
<StackPanel DataContext="{Binding /}" ...>
<MenuItem Header="{Binding MenuItem1}" OnClick="DisplayListBox" />
<MenuItem Header="{Binding MenuItem2}" OnClick="DisplayTextBox" />
<TextBox Visibility="Hidden" ... />
<ListBox Visibility="Hidden" ... />
</StackPanel>
</Grid>
with appropriate code for DisplayListBox and DisplayTextBox

Resources