我正在尝试创建一个包含图像和文本的按钮。但是,可以通过运行时后面的代码来更改图像。我的图片是png,每个人都在我的ResourceDictionary中:
<BitmapImage x:Key="iconLogIn" UriSource="/Images/Icons/lock.png" />
所以,我开始使用这种风格,没有Template或ContentTemplate。
<Style x:Key="MyTestButton" TargetType="{x:Type Button}">
<Setter Property="Height" Value="80" />
<Setter Property="Width" Value="100" />
</Style>
我的XAML是:
<Button x:Name="cmdOption1" Style="{StaticResource MyTestButton}" Margin="8" Click="cmdOption1_Click">
<Image Source="{DynamicResource iconLogIn}" />
</Button>
然后,我改变图像的代码是:
cmdOption1.Content = new Image() { Source = ((BitmapImage)FindResource("iconLogOut")) };
到目前为止,这都有效。
但是,只保留图像,我想在图像下面放置一个文本。
所以我读过this帖子,而HighCore的答案,选项#2,可能符合我的要求。但是,现在出现了一个新问题:
首先,这是一种新的风格,带有一个简单的模板
<Style x:Key="MyTestButton" TargetType="{x:Type Button}">
<Setter Property="Background" Value="#EEEEEE" />
<Setter Property="Foreground" Value="DarkSlateGray" />
<Setter Property="Height" Value="80" />
<Setter Property="Width" Value="100" />
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Padding" Value="2"/>
<Setter Property="FontSize" Value="12" />
<Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="BorderBrush" Value="DarkGray" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border>
<StackPanel>
<Image Source="{Binding Tag, RelativeSource={RelativeSource TemplatedParent}}" Height="50" Width="50"/>
<ContentPresenter Grid.Row="0" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" RecognizesAccessKey="True" />
</StackPanel>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Foreground" Value="DarkOrange" />
<Setter Property="OpacityMask" Value="#AA888888"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="Gray" />
<Setter Property="BorderBrush" Value="DarkGray" />
<Setter Property="Background" Value="White" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Style>
我的新XAML是:
<Button x:Name="cmdOption1" Tag="{StaticResource iconLogIn}" Content="LOG IN" Style="{StaticResource MyTestButton}" Margin="8" Click="cmdOption1_Click" />
*还有Tag =“{DynamicResource iconLogIn}”
我的代码背后改变了图像和文字:
cmdOption1.Tag = new Image() { Source = ((BitmapImage)FindResource("iconLogOut")) };
cmdOption1.Content = "LOG OUT";
这样,内容文本从“LOG IN”变为“LOG OUT”。但是图像不再显示,图像中没有任何图像,也没有抛出任何错误或异常。
我想知道解决方案是什么,发生了什么?为什么图像不会改变,但却消失了?
提前致谢。
答案 0 :(得分:5)
我认为您应该考虑采用更清晰的方法,使用MVVM,使用数据绑定和实现INotifyPropertyChanged。
首先:使用ViewModel定义用于定义ImageUri和Text的属性:
private Uri _myImageUriProperty;
public Uri MyImageUriProperty
{
get { return _myImageUriProperty; }
set { _myImageUriProperty = value; RaisePropertyChanged(() => MyImageUriProperty); }
}
private string _myTextBlockProperty;
public string MyTextBlockProperty
{
get { return _myTextBlockProperty; }
set { _myTextBlockProperty = value; RaisePropertyChanged(() => MyTextBlockProperty); }
}
此外:在按钮内,您可以使用占位符在按钮中放置多个UI元素。并从视图模型中将它们绑定到您的属性:
<Button x:Name="MyButton"
Click="MyButton_OnClick">
<StackPanel>
<Image x:Name="MyImage"
Source="{Binding MyImageUriProperty}" />
<TextBlock x:Name="MyTextBlock"
Text="{Binding MyTextBlockProperty}" />
</StackPanel>
</Button>
最后:使用codebehind中的click事件重置viewmodel中的值(您在代码隐藏的构造函数中设置为DataContext。PropertyChanged事件将触发UI中的更新。
private void MyButton_OnClick(object sender, RoutedEventArgs e)
{
ViewModel.MyImageUriProperty = new Uri("mynewuri.png", UriKind.Relative)
ViewModel.MyTextBlockProperty = "LogOut";
}
答案 1 :(得分:0)
您创建一个新的Image控件并将其指定给Button的Tag
属性。因此,ControlTemplate中的Image控件将其Source
属性设置为另一个Image控件。那不行。只需将资源中的BitmapImage分配给Tag
。
而不是
cmdOption1.Tag = new Image() { Source = ((BitmapImage)FindResource("iconLogOut")) };
你应该写这个:
cmdOption1.Tag = (BitmapImage)FindResource("iconLogOut");
答案 2 :(得分:0)
您的XAML工作正常。代码中唯一必须以这种方式设置Image
:
private void cmdOption1_Click(object sender, RoutedEventArgs e)
{
BitmapImage MyBitmapImage = ((BitmapImage)FindResource("iconLogOut"));
cmdOption1.Tag = MyBitmapImage;
cmdOption1.Content = "LOG OUT";
}
作为替代方法(仅用于比较方法),您可以通过函数Tag
使用此技巧而不使用FindChild<>
。你的模板部分:
<ControlTemplate TargetType="{x:Type Button}">
<Border>
<StackPanel>
<Image x:Name="MyContentImage" Source="{StaticResource iconLogIn}" Height="50" Width="50" />
<ContentPresenter Grid.Row="0" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" RecognizesAccessKey="True" />
</StackPanel>
</Border>
...
代码背后:
private void cmdOption1_Click(object sender, RoutedEventArgs e)
{
// Find the Image in template
Image MyContentImage = FindChild<Image>(cmdOption1, "MyContentImage");
MyContentImage.Source = ((BitmapImage)FindResource("iconLogOut"));
cmdOption1.Content = "LOG OUT";
}
FindChild<>
的列表:
public static T FindChild<T>(DependencyObject parent, string childName) where T : DependencyObject
{
if (parent == null)
{
return null;
}
T foundChild = null;
int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
T childType = child as T;
if (childType == null)
{
foundChild = FindChild<T>(child, childName);
if (foundChild != null) break;
}
else
if (!string.IsNullOrEmpty(childName))
{
var frameworkElement = child as FrameworkElement;
if (frameworkElement != null && frameworkElement.Name == childName)
{
foundChild = (T)child;
break;
}
else
{
foundChild = FindChild<T>(child, childName);
if (foundChild != null)
{
break;
}
}
}
else
{
foundChild = (T)child;
break;
}
}
return foundChild;
}