Reputation: 1907
I've been banging my head against this one for quite some time now, so I figured I'd ask here and see if someone can give me a hand. First off, I'm new to C#, XAML, and Windows 8 programming.
The basic thing I want to do is this: based on a change to the data model (which I'm doing in my little testbed project inside a Button_Click handler), I'd like to change the Foreground value in a <Style>
. I have had success in changing the Foreground value in a <TextBlock>
, but not on a <Style>
that operates on another <TextBlock>
.
Here's what I've done (pardon the verbosity, hopefully it makes sense):
I've got a class called MyColorDataSource that I'm using as a binding target in my MyPage.xaml page. Here's the relevant code from that class:
public class MyColorDataSource
{
private MyColors _brush1;
public MyColors Brush1
{
get { return _brush1; }
set { _brush1 = value; }
}
private MyColors _brush2;
public MyColors Brush2
{
get { return _brush2; }
set { _brush2 = value; }
}
public MyColorDataSource()
{
_brush1 = new MyColors();
_brush2 = new MyColors();
_brush1.BrushColor= new SolidColorBrush(Colors.DarkRed);
_brush2.BrushColor = new SolidColorBrush(Colors.Pink);
}
}
You'll notice that the variables in MyColorDataSource are of type MyColors. Here's the definition of that class:
public class MyColors : INotifyPropertyChanged
{
private SolidColorBrush _brushColor;
public SolidColorBrush BrushColor
{
get { return _brushColor; }
set {
if (value != _brushColor)
{
_brushColor = value;
NotifyPropertyChanged("BrushColor");
}
}
}
#region INotifyPropertyChanged Members
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
Debug.WriteLine("in NotifyPropertyChanged for " + propertyName);
}
}
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
Here's the XAML I've written, with two <TextBlock>
elements, one of which has its Foreground property bound directly to the datasource, and one of which uses a <Style>
which I'm trying to bind to the datasource. In MyPage.xaml:
<Page.Resources>
<Style TargetType="TextBlock" x:Key="BoundColor">
<Setter Property="Foreground" Value="{Binding Brush2.BrushColor}" />
</Style>
</Page.Resources>
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBlock x:Name="TextBlockToFormatWithBinding" Text="Direct Binding" Foreground="{Binding BrushColor}" />
<TextBlock x:Name="TextBlockToFormatWithStyleBinding" Text="Binding through style" Style="{StaticResource BoundColor}"/>
</StackPanel>
I've set up my DataSource variable in App.xaml.cs:
public sealed partial class App : Application
{
public static MyColorDataSource ColorDataSource;
public App()
{
this.InitializeComponent();
this.Suspending += this.OnSuspending;
ColorDataSource = new MyColorDataSource();
}
}
And I access the datasource in MyPage.xaml.cs:
public MyPage()
{
this.InitializeComponent();
App.ColorDataSource.Brush1.BrushColor = new SolidColorBrush(Colors.DarkOliveGreen);
App.ColorDataSource.Brush2.BrushColor = new SolidColorBrush(Colors.DarkKhaki);
this.DataContext = App.ColorDataSource;
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
TextBlockToFormatWithBinding.DataContext = App.ColorDataSource.Brush1;
// TextBlockToFormatWithStyleBinding.DataContext = App.ColorDataSource.Brush2; // tried this, it did nothing
}
If you're still with me, thank you! What's happening is that the first <TextBlock>
shows up as expected, in a DarkOliveGreen color. The second <TextBlock>
does not show up at all, and I'm assuming that its Foreground is defaulting to Black (the page background is also Black, so I can't see it). When I change the Foreground value in the <Style>
to something like "DarkRed", voila, it shows up as expected. So the <TextBlock>
is definitely there, but it's not getting its Foreground value from the DataModel as I want it to.
Has anyone out there run into this before, and if so, how did you resolve it? I really don't want to have to change each <TextBlock>
individually, as I intend to try to style items in a <ListView>
, and I really don't want to iterate over all of the <Item>
s and change their styling one by one. Having done a lot of HTML/CSS/jQuery, I'm used to being able to style things by class instead of iteratively, one at a time.
Thanks in advance for any help or advice you can offer.
Edit:
I did manage to run across a technique that gets me pretty close to what I'm trying to do here, partially with the help of the answer below (thanks again for that!). Hopefully if someone else comes along with a similar question, this will help out.
<SolidColorBrush x:Key="myBrush" Color="#330000FF" />
<Style TargetType="TextBlock" x:Key="BoundColor">
<Setter Property="Foreground" Value="{StaticResource myBrush}" />
</Style>
Then in my code-behind, I can do something like this:
((SolidColorBrush)Resources["myBrush"]).Color = Color.FromArgb(64, 255, 0, 0);
This doesn't put the color of the element in the data model, per se, but honestly does the color of an element really belong in the data model? I would generally say that it does not.
Note that I didn't have any luck making this technique work on the 'Visibility' <Setter>
I tried to put on it (I wanted the TextBlock to be hidden sometimes and visible sometimes), but for colors and other object-based <Setter>
s it worked just fine. My supposition based upon the little that I know is that this is true because Visibility is an enum, not class. So while I can put this in my <Page.Resources>
:
<Visibility x:Key="TextBlockVisibility">0</Visibility>
and this in the <Style>
:
<Setter Property="Visibility" Value="{StaticResource TextBlockVisibility}" />
I don't seem to be able to alter the value of the TextBlockVisibility
in such a way that the <Style>
will notice or be affected by the change in value.
Upvotes: 2
Views: 471
Reputation: 70671
Sorry, but you can't. Like a number of very useful WPF features (your code works just fine in a WPF program), this is AFAIK not supported on Windows Phone.
That said, you can work around the problem. The most obvious solution is to not use a style in the first place. Another solution is to use a "helper" object as the actual setter value in your style, which itself manages the binding via an attached property being set by the style's setter.
An example of this technique can be found at David Anson's web site, here: The taming of the phone [New SetterValueBindingHelper sample demonstrates its usefulness on Windows Phone 7 (and Silverlight 4)]
Using that example, your style setter might look something like this:
<Setter Property="delay:SetterValueBindingHelper.PropertyBinding">
<Setter.Value>
<delay:SetterValueBindingHelper
Property="Foreground"
Binding="{Binding Brush2.BrushColor}"/>
</Setter.Value>
</Setter>
(Note: I didn't actually test a working example for your code; you might have to fiddle a bit with the binding itself to get the data context right, as I'm not sure it will correctly inherit the context from the page with the extra indirection.)
Upvotes: 2