I'm having a problem with an installer from which I need to take a couple of fields of user input. Say that I have a Textboxes UI dialog in VS, in which I have set the first field's Property name to "URI". All the articles, StackOverflow posts etc that I can find are telling me I should access that value like so:
public override void Install(IDictionary stateSaver)
{
base.Install(stateSaver);
string uri = this.Context.Parameters["URI"];
}
but this is not working; all I am getting is an empty string. Why is that? What do I need to do differently?
This was a misconception partly due to examples I read passing the CustomAction Data property using the same name and capitalisation for the UI property name as for the CustomAction name. Specifically, I had interpreted it as meaning if one specified the value of 'Edit1Property' in a Textboxes UI dialog as "URI", the value entered would be passed to Context.Parameters["URI"]. This is missing a step. As per Alex's answer, specifically this article linked in the answer he suggested, one must also add each property that needs to be passed to the CustomActionData field for the Custom Action which uses it.
Thank you Alex.
Related
I have been working on this for days and I just don't get the concept of how Custom Actions work with Wix. Or at least I don't see how to do what I want.
I have several XML files that I want to read a value from and populate a property that gets displayed in a UI Dialog. Then when the install begins update that value in another XML file.
I need to be able to pass the filename including path of the local XML file and the node to search for and the key value pair to extract. I also need to pass what property needs to be updated.
I understand the CustomAction DLL concept. And that the session.CustomActionData["parametername"] syntax for passing in parameters. And the session["property"] = to set a property.
But I can't figure out the syntax of the wsx code to make it all happen. I read different examples doing it different ways?
I searched all the Google links out there an nothing seems to fit what I want to do?
You want this to be an immediate custom action not a deferred custom action so CustomActionData has no relevance to you.
What I would do is write a custom table like such:
Id [PK]
File (Formatted)
XPATH
Property
Here's an example:
mySearch
[SOMEPROPERTY]
/test[#'test'] (something like that, I hate xpath)
MYPROPERTY
You can use things like Property/FileSearch to have MSI's AppSearch resolve the location of a file and assign it to [SOMEPROPERTY]. Then you write a custom action scheduled after AppSearch to fetch this table's data, iterate it and fetch the attribute value (or element innertext) of each row and assign it to MYPROPERTY.
InstallShield gave this to me for free. I don't think WiX has a built in extension to do this. Maybe there is a community extension out there. It would probably take me an hour to write a prototype of this in C#/DTF.
I have a control which is a TreeView node that will always be set to the name of the PC you're currently running the tested software on. Therefore I need the Search Property for the control's Name property to be set to `Environment.MachineName like so:
The problem with this is that inside UIMap.Designer.cs I can see the generated code that this makes and it's trying to use Environment.MachineName as a string:
this.SearchProperties[WinTreeItem.PropertyNames.Name] = "Environment.MachineName";
Obviously this approach won't work, and it's not possible to manually edit UIMap.Designer.cs to change this. How can I make this work then?
The general approach is to use the UI Map editor to remove that search item. This should be possible from the window shown in the question. Then, in the test method that needs to do the search, add a statement something like
this.uimap.controlNames.SearchProperties[WinTreeItem.PropertyNames.Name]
= Environment.MachineName;
or
this.uimap.controlNames.SearchProperties.Add(name, Environment.MachineName;
The precise statement depends on the structure of the controls, so where I wrote .controlNames. it may need a series of dot-separated control names.
I'm using EWL and I have an EwfPage and when I type partial in the Info class I see:
partial void initDefaultOptionalParameterPackage( OptionalParameterPackage package )
and
partial void initUserDefaultOptionalParameterPackage( OptionalParameterPackage package )
I don't really see what they're used for. They also sound similar and I'm wondernig what the difference between them is.
They are both used if you want an optional parameter to default to something other than the default value of its C# data type. There are two significant differences:
initUserDefaultOptionalParameterPackage is called only when you are creating an Info object for the page; it is not called when the page is requested. If a request is made without a parameter value in the URL, the framework will fall back to the value specified in initDefaultOptionalParameterPackage or the data type default.
You can access AppTools.User from initUserDefaultOptionalParameterPackage if you meet the conditions specified in the doc comment for AppTools.User.
An example of when you might use initUserDefaultOptionalParameterPackage is a page that should default to showing information for the currently logged-in user but has a select list or something that lets you look at information for a different user.
I need to create a few tests for the user roles in a web application. To minimize the description, one of the tests involves checking if a menu entry is displayed or not for an user.
For this test, I use a table called UserRoles, that looks like this:
sUserName bDoesntHaveMenuX
User1 1
User2 0
User3 1
bDoesntHaveMenuX is of type bit.
I have a class derived from ValidationRule that checks if a certain text is present in a page, based on a XPath expression to locate the node where to look for the text.
The public properties of this class are:
string XPathExpression
string Text
bool FailIfFound
The last one dictates if the rule should fail if the text is found or not found.
In the test I added a datasource for the table mentioned in the beginning, called DS.
For the request I'm interested in I added a new instance of my validation rule class, with the following values:
Text=MenuX
XPathExpression=//div[#id='menu']//td
FailIfFound={{DS.UserRoles.bDoesntHaveMenuX}}
Unfortunately, this doesn't work.
The reason seems to be that the data binding process creates a context variable
DS.UserRoles.bDoesntHaveMenuX has the value "False" or "True". The value is a string, so the binding results in a casting error.
My options, as far as I can think of, are:
Change the validation rule to accept strings for FailIfFound. Not a valid
option, for 2 reasons: it's a hack and the same rule is used in
other places.
Make a new validation rule that will use the above mentioned one,
and implement the FailIfFound as string. I also don't like this, for
the same reason as above. It's a hack.
Make the test coded and do the proper cast before passing the data
to the validation rule. I don't like this one because I prefer to
have the test as coded only if there is no other way.
Which brings me to the question. Is there another way?
Thank you.
So the fundamental issue is that you have no control over how the data-binding treats the 'bit' data type, and it's getting converted to string instead of bool.
The only solution I can think of (which is sadly still a bit of a hack, but not so egregious as changing FailIfFound to string) is to create a WebTestPlugin, and in the PreRequestDataBinding or PreRequest event, convert the value from string to bool. Don't forget to add the plugin to your test(s) (easy mistake I have made).
Then when the validation rule is created it should pick up the nice new bool value and work correctly.
e.g.
string val = e.WebTest.Context["DS.UserRoles.bDoesntHaveMenuX"].ToString();
e.WebTest.Context["DS.UserRoles.bDoesntHaveMenuX"] = (val == "True");
I didn't actually try this... hope it works.
EDIT: round two... a better solution
Change the FailIfFound property to string (in a subclass as you mentioned), so it can work properly with data-binding.
Implement a TypeConverter that provides a dropdown list of valid values for the property in the rule's PropertyGrid (True, False), so in the GUI it looks identical to the rule having FailIfFound as a bool. You can still type your own value into the box when necessary (e.g. for data-binding).
Add the path of the .dll containing the TypeConverter code to your test project's References section.
This is what I have started doing and it is much more satisfying than having to type 'True' or 'False' in the property's edit box.
My typical application has a couple of textboxes, checkbuttons, radiobuttons, and so. I always want to load the settings the user used the last time when the program starts, and also want to save the settings as the users clicks "Save settings" or closes the application. When the user attempts to save the settings, I'll have to check each of the controls for input errors (sometimes they have to have a max length, other times only caps, other times other things, there isn't a rule for them all, everytime it'll be different), and only if everything's OK i'll let him save the options. If there is something wrong, no option is saved and my errorcontrol provider will pop up a description of the input type info that should be put in that control.
I've been designing this from scratch for all my projects, but it's being a pain to do it. So I'd thought maybe now was the time to do some library to help me. I thought initially that maybe it'd be a good idea to have all the controls on my form that are going to be part of this save/load process to have an attribute associated with them, something like this
public delegate bool InputIsOkHandler();
public class OptionsAttribute : Attribute {
public Control controlRef;
public InputIsOkHandler IsInputOk;
public string errorMessageToShowOnErrorProvider;
public OptionsAttribute(Control controlRef, InputIsOkHandler inputHandler, string errMessage) {
...
}
}
The main problem here is that when I declare the attribute on a given var:
[Options(...)]
TextBox textBox1 = new TextBox();
I'll get
Error 1 An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type.
So I guess this approach isn't the best one. What would you guys do in this situation? Would you use attributes? Would you use other mechanisms?
Thanks
Do you know that .NET already includes such a system since 2.0? See MSDN, CodeProject and this white paper from WestWind.
The Personalization and User Profiles supported in ASP.NET 2.0 can be a nice way to achieve your goal.
You can check this MSDN article for a overview Personalization in ASP.NET 2.0