5

在我的一个对话框中,我有以下控制:

<Control Id="EnvironmentComboBox" Type="ComboBox" Sorted="yes" ComboList="yes" Property="ENVIRONMENT" X="25" Y="110" Width="200" Height="15" />

我在其他地方填充 ComboBox,如下所示:

<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>

但是,如果我没有创建 ComboBox 位,MSI 仍将构建,并且在安装期间将失败 (2205)。因此,我想强制要求拥有一个名为 ENVIRONMENT 的属性。我尝试将如下所示的 PropertyRef 添加到我的对话框中:

<PropertyRef Id="ENVIRONMENT" />

但是,这似乎并没有使<ComboBox Proeprty="ENVIRONMENT">. 它将获取一个常规属性 ( <Property Id="ENVIRONMENT" Value="test" />),但这并没有太大帮助。

有什么方法可以要求ComboBox定义 a 吗?

编辑: 为澄清起见,我打算将 ComboBox 定义与 Control 定义分开,以便可以重用对话框。

4

3 回答 3

4

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 =).

于 2012-11-20T16:31:13.913 回答
1

我怀疑<PropertyRef>设计的方式是只获取属性的“直接”定义,即<Property>元素。只是在其<ComboBox>属性中提到了属性名称,这不被视为属性定义。

将“直接”属性定义添加到您的示例中,它应该可以工作:

<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>

并在另一个地方用元素引用它<PropertyRef>——就像你尝试的那样。据我所知,这样的定义不会损害组合框部分,并且通过适当的片段包含,您将是安全的。

或者,您可以使用 element 引用整个<UI>元素<UIRef>- 它应该具有相同的效果。

于 2012-11-12T16:01:20.560 回答
0

为什么不在元素内定义您的组合框?像这样:

<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.

于 2012-11-16T12:24:43.940 回答