ProgressBar and OpacityMask
Hi,
I'm trying to customize a ProgressBar, but I have two use-cases that are a bit complicated to me:
To answer both these use-cases, I've thought my template could be as follow:
The idea is that "PART_Track" represents the empty bar and "FullTrack" represents the bar completely full, both filling the parent Grid. My idea is to use "PART_Indicator" as an OpacityMask, so that I only display the proper proportion of the filled bar.
I can't get it to work on Blend. The bar is completely white, except if the ProgressBar::Value is set to 0 (it becomes fully black). I've tried several combinations but that's the closest I got.
Then, when I use that Template with the XamlPlayer, the application crashes. Here is the content of the file in question (can't upload it directly apparently):
Apparently, it has to do with the OpacityMask, and particularly VisualBrush (I've tried to use a LinearGradientBrush with success elsewhere). Do you see what might go wrong?
Obviously, that would only solve my first use case, but I think I could solve the second use-case with the same kind of operation (using Circle arcs as OpacityMask rather than a simple Rectangle). What do you think?
Thank you,
Alexandre.
Edit:
I confirm there's something about VisualBrush that Noesis does not like. I've made a simpler sample to use them and both my application and the XamlPlay crash when I try using these. That's an important feature to me, what else should I use?
I'm trying to customize a ProgressBar, but I have two use-cases that are a bit complicated to me:
- Segmented bar like that one
- Segmented circular bar
To answer both these use-cases, I've thought my template could be as follow:
Code: Select all
<ControlTemplate>
<Grid>
<Rectangle x:Name="PART_Indicator" Fill="Pink" HorizontalAlignment="Left"/>
<Border x:Name="PART_Track" Background="Black"/>
<Grid>
<Grid.OpacityMask>
<VisualBrush Visual="{Binding ElementName=PART_Indicator}"/>
</Grid.OpacityMask>
<Border x:Name="FullTrack" Background="White"/>
</Grid>
</Grid>
</ControlTemplate>
I can't get it to work on Blend. The bar is completely white, except if the ProgressBar::Value is set to 0 (it becomes fully black). I've tried several combinations but that's the closest I got.
Then, when I use that Template with the XamlPlayer, the application crashes. Here is the content of the file in question (can't upload it directly apparently):
Code: Select all
<StackPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Background="Gray" Orientation="Vertical" VerticalAlignment="Center">
<Slider x:Name="PART_Slider" Orientation="Horizontal" Height="22" Maximum="100" Value="4" Width="300"/>
<ProgressBar Height="29.532" Value="{Binding Value, ElementName=PART_Slider}" Width="300">
<ProgressBar.Style>
<Style TargetType="{x:Type ProgressBar}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<Rectangle x:Name="PART_Indicator" Fill="Pink" HorizontalAlignment="Left"/>
<Border x:Name="PART_Track" Background="Black"/>
<Grid>
<Grid.OpacityMask>
<VisualBrush Visual="{Binding ElementName=PART_Indicator}"/>
</Grid.OpacityMask>
<Rectangle Fill="White"/>
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ProgressBar.Style>
</ProgressBar>
</StackPanel>
Obviously, that would only solve my first use case, but I think I could solve the second use-case with the same kind of operation (using Circle arcs as OpacityMask rather than a simple Rectangle). What do you think?
Thank you,
Alexandre.
Edit:
I confirm there's something about VisualBrush that Noesis does not like. I've made a simpler sample to use them and both my application and the XamlPlay crash when I try using these. That's an important feature to me, what else should I use?
Last edited by ZanAlex on 04 Sep 2015, 11:30, edited 1 time in total.
-
sfernandez
Site Admin
- Posts: 3184
- Joined:
Re: ProgressBar and OpacityMask
Hi Alex,
To accomplish the star rating bar you just need to play with the Clip property
VisualBrush on the other side has some limitations. We only support it when VisualBrush.Visual binds to a named element in the tree, it doesn't work yet when you define the visual tree in the Visual property.
Anyway, if you get a crash using a VisualBrush, please create a ticket in our bugtracker so we can manage this situation and provide a clean message.
To accomplish the star rating bar you just need to play with the Clip property
Code: Select all
<ProgressBar Width="300" VerticalAlignment="Center" Value="51" BorderBrush="#FF905101" Foreground="#FFFF9E25">
<ProgressBar.Template>
<ControlTemplate TargetType="ProgressBar">
<ControlTemplate.Resources>
<Geometry x:Key="Stars">
M16.5,1L20.2,12 32,12 22.6,19 26.5,31 16.5,23.6 6.5,31 10.4,19 1,12 12.8,12z
M53.5000648498535,1L57.2000648498535,12 69.0000648498535,12 59.6000648498535,19 63.5000648498535,31 53.5000648498535,23.6 43.5000648498535,31 47.4000648498535,19 38.0000648498535,12 49.8000648498535,12z
M90.500129699707,1L94.200129699707,12 106.000129699707,12 96.600129699707,19 100.500129699707,31 90.500129699707,23.6 80.500129699707,31 84.400129699707,19 75.000129699707,12 86.800129699707,12z
M127.500194549561,1L131.200194549561,12 143.000194549561,12 133.600194549561,19 137.500194549561,31 127.500194549561,23.6 117.500194549561,31 121.400194549561,19 112.000194549561,12 123.800194549561,12z
M164.500259399414,1L168.200259399414,12 180.000259399414,12 170.600259399414,19 174.500259399414,31 164.500259399414,23.6 154.500259399414,31 158.400259399414,19 149.000259399414,12 160.800259399414,12z
</Geometry>
</ControlTemplate.Resources>
<Viewbox>
<Grid>
<Border x:Name="PART_Track"/>
<Border x:Name="PART_Indicator" HorizontalAlignment="Left"
Background="{TemplateBinding Foreground}" Clip="{StaticResource Stars}"/>
<Path Data="{StaticResource Stars}" Stretch="Fill"
Stroke="{TemplateBinding BorderBrush}" StrokeThickness="2" StrokeLineJoin="Round"/>
</Grid>
</Viewbox>
</ControlTemplate>
</ProgressBar.Template>
</ProgressBar>
Anyway, if you get a crash using a VisualBrush, please create a ticket in our bugtracker so we can manage this situation and provide a clean message.
Re: ProgressBar and OpacityMask
Hi,
Thank you for your answer!
I should have asked you before, I'll try that solution a bit later today. Thank you!
I'm not sure I understand what you mean by bound to a named element in the tree. Isn't it what I've tried to do in my sample?
Many thanks
Thank you for your answer!
I should have asked you before, I'll try that solution a bit later today. Thank you!
I'm not sure I understand what you mean by bound to a named element in the tree. Isn't it what I've tried to do in my sample?
Many thanks
-
sfernandez
Site Admin
- Posts: 3184
- Joined:
Re: ProgressBar and OpacityMask
If this is crashing, this is not a known bug:
Could you please report it in the bugtracker? Thanks.
Code: Select all
<VisualBrush Visual="{Binding ElementName=PART_Indicator}"/>
Re: ProgressBar and OpacityMask
Hi,
To achieve my second goal, I think I'll have to create a new Control, deriving from RangeBase that will use the Value property to create the proper arc and use it as a Clip for the PART_Indicator Border.
I was wondering if there was something I should be careful with in the RangeBase class. I'll override OnTemplateChanged (to retrieve the Track/Indicator and add my clipping arc to the Clip property of both) and OnValueChanged (to update my clipping arc). Should I call these in my override?
Many thanks
EDIT:
I've managed to get a nice result by applying what I've described earlier in that post. However, I still have a question.
What I'm doing is:
- Getting PART_Indicator
- Getting its Clip
- Creating a CombinedGeometry containing PART_Indicator's Clip and the arc I'm computing when the Value changes
- Using this CombinedGeometry as a PART_Indicator's Clip
If everything worked as I expected, I should have to simply update the arc as the value changes and PART_Indicator's Clip should be updated as expected. But what happens is that the CombinedGeometry does not change while I'm updating my arc. Everytime the Value changes, I have to create another CombinedGeometry and apply it again to PART_Indicator. That feels really ineffective. Is it an expected behaviour? I've tried to keep the same CombinedGeometry, change one of its geometry with SetGeometry1/2 and apply that result to PART_Indicator but that does not change anything either.
To achieve my second goal, I think I'll have to create a new Control, deriving from RangeBase that will use the Value property to create the proper arc and use it as a Clip for the PART_Indicator Border.
I was wondering if there was something I should be careful with in the RangeBase class. I'll override OnTemplateChanged (to retrieve the Track/Indicator and add my clipping arc to the Clip property of both) and OnValueChanged (to update my clipping arc). Should I call these in my override?
Many thanks
EDIT:
I've managed to get a nice result by applying what I've described earlier in that post. However, I still have a question.
What I'm doing is:
- Getting PART_Indicator
- Getting its Clip
- Creating a CombinedGeometry containing PART_Indicator's Clip and the arc I'm computing when the Value changes
- Using this CombinedGeometry as a PART_Indicator's Clip
If everything worked as I expected, I should have to simply update the arc as the value changes and PART_Indicator's Clip should be updated as expected. But what happens is that the CombinedGeometry does not change while I'm updating my arc. Everytime the Value changes, I have to create another CombinedGeometry and apply it again to PART_Indicator. That feels really ineffective. Is it an expected behaviour? I've tried to keep the same CombinedGeometry, change one of its geometry with SetGeometry1/2 and apply that result to PART_Indicator but that does not change anything either.
-
sfernandez
Site Admin
- Posts: 3184
- Joined:
Re: ProgressBar and OpacityMask
I confirmed that we have a bug with the CombinedGeometry, and changes in the Geometry1 or Geometry2 objects are not correctly notified, so render is not updated.
Anyway, CombinedGeometry is not fully implemented, and in case you want to use a geometry intersection, I suggest you to change the template to apply the clip geometries to two nested borders (the desired effect will be the same):
The inner border, will be clipped with the geometry of the segments. That geometry won't change.
The outer border, PART_Indicator, will be clipped with the calculated arc geometry. That geometry will change as RangeBase.Value changes. You can use a StreamGeometry for this purpose, and OnValueChanged you will only update the contents as follows:
Is this what you need?
Anyway, CombinedGeometry is not fully implemented, and in case you want to use a geometry intersection, I suggest you to change the template to apply the clip geometries to two nested borders (the desired effect will be the same):
Code: Select all
...
<Border x:Name="PART_Indicator">
<Border Background="{TemplateBinding Foreground}" Clip="{StaticResource segmentsGeometry}"/>
</Border>
...
The outer border, PART_Indicator, will be clipped with the calculated arc geometry. That geometry will change as RangeBase.Value changes. You can use a StreamGeometry for this purpose, and OnValueChanged you will only update the contents as follows:
Code: Select all
void MyCircularProgressBar::OnValueChanged(NsFloat32 oldValue, NsFloat32 newValue)
{
// assuming you cached PART_Indicator clip geometry in the OnTemplateChanged()
StreamGeometryContext context = _indicatorClipGeometry->Open();
context.BeginFigure(...);
...
context.Close();
}
Re: ProgressBar and OpacityMask
That's sounds like a better idea than what I have at the moment. I will change it accordingly, thanks a lot!
EDIT: It does work and make the code cleaner. Thanks!
EDIT: It does work and make the code cleaner. Thanks!
Who is online
Users browsing this forum: Ahrefs [Bot], Semrush [Bot] and 1 guest