WPF中使用自定义转换器绑定ImageSource

12

我有一个简单的下拉框模板,结构如下:

<ComboBox DockPanel.Dock="Left" MinWidth="100" MaxHeight="24"
          ItemsSource="{Binding Actions}">
    <ComboBox.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding Name}" Width="100" />
                <Image Source="{Binding Converter={StaticResource TypeConverter}}" />
            </StackPanel>
        </DataTemplate>
    </ComboBox.ItemTemplate>
</ComboBox>

因此,如果我使用这段代码,一切都能正常工作:

<TextBlock Text="{Binding Name}" Width="100" />
<!--<Image Source="{Binding Converter={StaticResource TypeConverter}}" /> -->
<Image Source="{StaticResource SecurityImage}" />

但是如果我使用转换器,它就不再起作用了。 这是转换器,但我不知道如何从那里引用静态资源...

public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
    var type = (Action)value;
    var img = new BitmapImage();
    switch (type.ActionType)
    {
        case ActionType.Security:
            img.UriSource = new Uri("StructureImage", UriKind.Relative);
            break;
        case ActionType.Structural:
            img.UriSource = new Uri("SecurityImage", UriKind.Relative);
            break;
    }

    return img;
}

我更新了我的帖子来回答你的问题。 - Jakob Christensen
2个回答

20

尝试使用Josh编写的Switch Converter,应该适合您:

SwitchConverter -

XAML的“switch语句” - http://josheinstein.com/blog/index.php/2010/06/switchconverter-a-switch-statement-for-xaml/

不需要编写自己的转换器,您的代码将如下所示 -

<Grid.Resources>  
    <e:SwitchConverter x:Key="ActionIcons">  
        <e:SwitchCase When="Security" Then="SecurithImage.png" />  
        <e:SwitchCase When="Structural" Then="StructureImage.png" />             
    </e:SwitchConverter>  
</Grid.Resources>  

<Image Source="{Binding Converter={StaticResource ActionIcons}}" />  

更新1:

这里是SwitchConverter的代码,因为Josh的网站似乎无法访问 -

/// <summary>
/// A converter that accepts <see cref="SwitchConverterCase"/>s and converts them to the 
/// Then property of the case.
/// </summary>
[ContentProperty("Cases")]
public class SwitchConverter : IValueConverter
{
    // Converter instances.
    List<SwitchConverterCase> _cases;

    #region Public Properties.
    /// <summary>
    /// Gets or sets an array of <see cref="SwitchConverterCase"/>s that this converter can use to produde values from.
    /// </summary>
    public List<SwitchConverterCase> Cases { get { return _cases; } set { _cases = value; } }
    #endregion
    #region Construction.
    /// <summary>
    /// Initializes a new instance of the <see cref="SwitchConverter"/> class.
    /// </summary>
    public SwitchConverter()
    {
        // Create the cases array.
        _cases = new List<SwitchConverterCase>();
    }
    #endregion

    /// <summary>
    /// Converts a value.
    /// </summary>
    /// <param name="value">The value produced by the binding source.</param>
    /// <param name="targetType">The type of the binding target property.</param>
    /// <param name="parameter">The converter parameter to use.</param>
    /// <param name="culture">The culture to use in the converter.</param>
    /// <returns>
    /// A converted value. If the method returns null, the valid null value is used.
    /// </returns>
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        // This will be the results of the operation.
        object results = null;

        // I'm only willing to convert SwitchConverterCases in this converter and no nulls!
        if (value == null) throw new ArgumentNullException("value");

        // I need to find out if the case that matches this value actually exists in this converters cases collection.
        if (_cases != null && _cases.Count > 0)
            for (int i = 0; i < _cases.Count; i++)
            {
                // Get a reference to this case.
                SwitchConverterCase targetCase = _cases[i];

                // Check to see if the value is the cases When parameter.
                if (value == targetCase || value.ToString().ToUpper() == targetCase.When.ToString().ToUpper())
                {
                    // We've got what we want, the results can now be set to the Then property
                    // of the case we're on.
                    results = targetCase.Then;

                    // All done, get out of the loop.
                    break;
                }
            }

        // return the results.
        return results;
    }

    /// <summary>
    /// Converts a value.
    /// </summary>
    /// <param name="value">The value that is produced by the binding target.</param>
    /// <param name="targetType">The type to convert to.</param>
    /// <param name="parameter">The converter parameter to use.</param>
    /// <param name="culture">The culture to use in the converter.</param>
    /// <returns>
    /// A converted value. If the method returns null, the valid null value is used.
    /// </returns>
    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

/// <summary>
/// Represents a case for a switch converter.
/// </summary>
[ContentProperty("Then")]
public class SwitchConverterCase
{
    // case instances.
    string _when;
    object _then;

    #region Public Properties.
    /// <summary>
    /// Gets or sets the condition of the case.
    /// </summary>
    public string When { get { return _when; } set { _when = value; } }
    /// <summary>
    /// Gets or sets the results of this case when run through a <see cref="SwitchConverter"/>
    /// </summary>
    public object Then { get { return _then; } set { _then = value; } }
    #endregion
    #region Construction.
    /// <summary>
    /// Switches the converter.
    /// </summary>
    public SwitchConverterCase()
    {
    }
    /// <summary>
    /// Initializes a new instance of the <see cref="SwitchConverterCase"/> class.
    /// </summary>
    /// <param name="when">The condition of the case.</param>
    /// <param name="then">The results of this case when run through a <see cref="SwitchConverter"/>.</param>
    public SwitchConverterCase(string when, object then)
    {
        // Hook up the instances.
        this._then = then;
        this._when = when;
    }
    #endregion

    /// <summary>
    /// Returns a <see cref="System.String"/> that represents this instance.
    /// </summary>
    /// <returns>
    /// A <see cref="System.String"/> that represents this instance.
    /// </returns>
    public override string ToString()
    {
        return string.Format("When={0}; Then={1}", When.ToString(), Then.ToString());
    }
}

更新2:

微软参考源代码提供了另一种 SwitchConverter 实现。


链接挂了!你还有SwitchConverter的代码吗? - Chrisjan Lodewyks
链接已失效,但您可以在此处访问文章:http://web.archive.org/web/20120202000409/http://josheinstein.com/blog/index.php/2010/06/switchconverter-a-switch-statement-for-xaml - akjoshi
@akjoshi 非常感谢! - Chrisjan Lodewyks
@ChrisjanLodewyks 非常感谢您指出这一点,我很高兴能帮助大家。 - akjoshi
1
@akjoshi,你忘记绑定条件(MyCondition)了:应该是<Image Source="{Binding MyCondition Converter={StaticResource ActionIcons}}" />。 - Stacked

6
当使用Image.UriSource时,如果图像已添加到您的项目并且它们的“Build Action”设置为“Resource”,则需要指定相对文件路径。例如,如果您在Visual Studio中的项目文件夹“images”中放置了图像,则可以按以下方式引用这些图像:
img.UriSource = new Uri("/Images/StructureImage.png", UriKind.Relative);

如果图像不是作为资源构建的,您必须使用完整的文件路径,即:
img.UriSource = new Uri("http://server/Images/StructureImage.png", UriKind.Absolute);

编辑:

如果您将图片放在应用程序的资源字典中,则始终可以按以下方式访问它:

Application.Current.Resources["StructureImage"];

如果你将资源放在其他地方,你可以使用 IMultiValueConverter 替代 IValueConverter 来创建你的转换器。这样,你的类型转换器会类似于以下内容:
class TestValueConverter : IMultiValueConverter
{
    #region IMultiValueConverter Members

    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        // Validation of parameters goes here...

        var type = (Action) values[0];
        var image1 = values[1];
        var image2 = values[2];

        if (type.ActionType == ActionType.Security)
        {
            return image1;
        }
        else
        {
            return image2;
        }
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    #endregion
}

您的XAML将类似于以下内容:

    <Image>
        <Image.Source>
            <MultiBinding Converter="{StaticResource testValueConverter}">
                <Binding Path="Action" />
                <Binding Source="{StaticResource SecurityImage}" />
                <Binding Source="{StaticResource StructureImage}" />
            </MultiBinding>
        </Image.Source>
    </Image>

最后,这就是您定义资源的方式:
<imaging:BitmapImage x:Key="StructureImage" UriSource="StructureImage.png" />
<imaging:BitmapImage x:Key="SecurityImage" UriSource="SecurityImage.png" />
<local:TestValueConverter x:Key="testValueConverter" />

以上代码未经测试!

1
好的,但是如果我想使用字典条目中的键来引用它们呢? - Raffaeu
最佳答案在我看来 - Wingjam

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接