Is it possible to nest a Progress bar into a combobox or the other way around. I want to be able to type into the combo box and hit a button and the progress bar shows the progress of the event, like in Windows Explorer.
EDIT: I need the code in Visual Basic.NET 3.5 Thanks.
Here's one way to do it, basically what I've done is:
Subclass ComboBox and add IsProgressVisible and ProgressValue dependency properties
Add a green rectangle the the ComboBox control template exactly behind the editable area
Bind the rectangle visibility to IsProgressVisible and the rectangle width (using a ScaleTransform) to ProgressValue
First the new control code:
public class ProgressCombo : ComboBox
{
public static readonly DependencyProperty IsProgressVisibleProperty =
DependencyProperty.Register("IsProgressVisible", typeof(bool), typeof(ProgressCombo));
public bool IsProgressVisible
{
get { return (bool)GetValue(IsProgressVisibleProperty); }
set { SetValue(IsProgressVisibleProperty, value); }
}
public static readonly DependencyProperty ProgressValueProperty =
DependencyProperty.Register("ProgressValue", typeof(double), typeof(ProgressCombo));
public double ProgressValue
{
get { return (double)GetValue(ProgressValueProperty); }
set { SetValue(ProgressValueProperty, value); }
}
}
There's also a value converter we'll use:
public class FromPercentConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return ((double)value) / 100;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Now take the combo box sample style from MSDN (.net 3.5 version, not 4) from http://msdn.microsoft.com/en-us/library/ms750638%28VS.90%29.aspx
Add an xmlns:l definition to your own assembly
Now change <Style x:Key="{x:Type ComboBox}" TargetType="ComboBox"> to <Style x:Key="{x:Type l:ProgressCombo}" TargetType="l:ProgressCombo">
Change <ControlTemplate TargetType="l:ComboBox"> To:
<ControlTemplate TargetType="l:ProgressCombo">
<ControlTemplate.Resources>
<BooleanToVisibilityConverter x:Key="Bool2Vis"/>
<l:FromPercentConverter x:Key="FromPercent"/>
</ControlTemplate.Resources>
Locate the line <ContentPresenter and add before it:
<Rectangle
Fill="LightGreen"
Margin="3,3,23,3"
Visibility="{TemplateBinding IsProgressVisible, Converter={StaticResource Bool2Vis}}">
<Rectangle.RenderTransform>
<ScaleTransform ScaleX="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ProgressValue, Converter={StaticResource FromPercent}}"/>
</Rectangle.RenderTransform>
</Rectangle>
And that's it
I had a similar sort of requirement for a different reason (i had a combo that was auto-populating after a network scan). See if this question & answer helps you: WPF ComboBox - showing something different when no items are bound
Related
I have something like this:
public class Member {
public int Id { get; set; }
public string Name { get; set; }
public string Age { get; set; }
...
}
List<Member> members = new List<Member>{ new Member(Id = 1, Name="Chuck", Age="32"), new Member(Id = 2, Name="Eve", Age="10")};
Listbox1.ItemsSource = members;
How can I change the background of items in the ListBox, if the age is less than 18?
Setting the background of a ListBoxItem can be done by changing the item container style. Conditional setting of the color however requires a IValueConverter. You can write your own for checking Age. Please consider making Age a property of type int, otherwise you will have to parse it on each conversion from string.
public class AgeToColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return int.Parse((string)value) < 18 ? new SolidColorBrush(Colors.Red) : new SolidColorBrush(Colors.Blue);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new InvalidOperationException("This is a one-way conversion.");
}
}
XAML solution
In XAML you would create an instance of your converter in the resouces of the list box or any other resource dictionary. Then you would bind the ItemsSource to your members property and add a custom items container style. The style is based on the default style for the ListBoxItem. In it you bind the Background property to Age with your custom converter, which will convert the age string to a solid color brush.
<ListBox x:Name="Listbox1" ItemsSource="{Binding members}">
<ListBox.Resources>
<local:AgeToColorConverter x:Key="AgeToColorConverter"/>
</ListBox.Resources>
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}" BasedOn="{StaticResource {x:Type ListBoxItem}}">
<Setter Property="Background" Value="{Binding Age, Converter={StaticResource AgeToColorConverter}}"/>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
Code-Behind solution
Suppose you defined your list box in XAML like this.
<ListBox x:Name="Listbox1"/>
Then the same as for the XAML solution applies to this code, except for we create the converter as a local variable and the items source is directly assigned instead of a binding.
var ageToColorConverter = new AgeToColorConverter();
var baseItemContainerStyle = (Style)FindResource(typeof(ListBoxItem));
var itemContainerStyle = new Style(typeof(ListBoxItem), baseItemContainerStyle);
var backgroundSetter = new Setter(BackgroundProperty, new Binding("Age") { Converter = ageToColorConverter });
itemContainerStyle.Setters.Add(backgroundSetter);
Listbox1.ItemContainerStyle = itemContainerStyle;
Listbox1.ItemsSource = members;
Is there a way to bind underline?? I'm trying to achieve the following:
I have VievModel with a bool property:
public bool HomeButtonUnderline { get; set; } = false;
I would then like to control this property in the following function:
public void Home() {
//CurrentPage = ApplicationPage.Home;
//HomeButtonForeground = new SolidColorBrush(Colors.White);
HomeButtonUnderline = true;
SettingsButtonUnderline = false;
}
I could then utilize this control in XAML:
<Button>
<TextBlock Command="{Binding HomeNavCommand}" Underline="{Binding HomeButtonUnderline}"/>
</Button>
The problem is that there isn't an 'Underline' property, instead it is handled by 'TextDecorations':
<Button>
<TextBlock TextDecorations="Underline">
</Button>
So is there a way to control underline using MVVM or even without it??
You may use a DataTrigger in a Style for the TextBlock:
<Button Command="{Binding HomeNavCommand}">
<TextBlock Text="Home">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding HomeButtonUnderline}" Value="True">
<Setter Property="TextDecorations" Value="Underline"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Button>
Use a Converter to convert your model types (in this case, a bool) to the UI types (in this case, a TextDecoration).
public class UnderlineConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null)
return null;
return System.Convert.ToBoolean(value) ? TextDecorations.Underline : null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
And then use it in your binding (after creating a resource in your Window, UserControl App.xaml or whereever you feel is the right place; for this sample I'm just putting it in the button resources)
<Button Command="{Binding HomeNavCommand}">
<Button.Resources>
<local:UnderlineConverter x:Key="UnderlineConverter" />
</Button.Resources>
<TextBlock TextDecorations="{Binding HomeButtonUnderline, Converter={StaticResource UnderlineConverter}"/>
</Button>
Of course, another way to handle this is for your view model to be the right type for the view:
public TextDecorationCollection HomeButtonUnderline { get; set; }
HomeButtonUnderline is already GUI-orientated, so there's probably some code in your viewmodel that assigns a value from the model, so it might as well also convert from bool to TextDecorations.Underline.
This method keeps all the logic in the same file, unlike a converter, although a converter is more reusable. It also has the advantage of being easily unit-testable (unlike a data trigger).
I have a bunch of textblock objects in my program, and the foreground color of all of these is white. I want to find out what causes it to be this way.
Is there any means in Visual Studio of tracing back the origin of a property value, whether by template or containing object?
EDIT:
After issuing a bounty, this question got a lot of interesting responses with a variety of proposed approaches. I think there's something there, but so far I have not been able to figure out how to apply any of these more effectively than just trial and error hunting through the code. If anybody wants to take up the charge and advance one of the responses as useful, I'm watching.
I tend to use Snoop to poke around the visual tree when I need to understand where bound values are coming from. You can use it to look at your control properties and follow either bindings back to DataContexts or walk up and down the tree to see where a value might be inherited from.
Fire up Snoop, drag the crosshairs over your WPF application (or refresh the process list and select from the dropdown), then position your mouse pointer over your TextBlock control and Ctrl-Shift-LeftClick. Snoop will zoom the visual tree in on your control and allow you to look at or edit your control properties.
There is already an answer showing a managed tool.
Here I'm expanding my comment about debugging the InitializeComponent.
Let's assume a basic xaml as a proof of concept.
<Grid>
<Grid.Resources>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="Blue" />
</Style>
</Grid.Resources>
<Border BorderBrush="Black" BorderThickness="1" HorizontalAlignment="Left" Height="200" Margin="10,25,0,0" VerticalAlignment="Top" Width="199">
<TextBlock Name="analysethis" Text = "why is it blue?" TextWrapping="Wrap" />
</Border>
</Grid>
Intercepting the event of the Foreground Property of the TextBlock is difficult because we can't subclass TextBlock (otherwise the style will be lost)
and we can't override a Freezable. Should I have no other solutions, I would set a breakpoint on the following converter
[ValueConversion(typeof(Color), typeof(String))]
public class DebugConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (!(value is Color)) return null; // << BP here
var result = value.ToString();
return result;
}
linking it to the TextBlock with a static resource
<TextBlock Name="analysethis" TextWrapping="Wrap" Margin="0,81,0,75"
Text="{Binding RelativeSource={RelativeSource Self},
Path=Foreground.Color, Converter={StaticResource ColConv} }" >
After starting the Debug, one can see the initial default Foreground assigned to "#FF000000" and the starting condition is in the stack
System.Xaml.dll!System.Xaml.XamlObjectWriter.Logic_DoAssignmentToParentProperty(MS.Internal.Xaml.Context.ObjectWriterContext ctx) + 0xc6 byte
System.Xaml.dll!System.Xaml.XamlObjectWriter.Logic_AssignProvidedValue(MS.Internal.Xaml.Context.ObjectWriterContext ctx) + 0x37 byte
Then there is a second hit on the breakpoint and the source of the actual Foreground color can be finally found in the stack
> PresentationFramework.dll!System.Windows.StyleHelper.DoStyleInvalidations(System.Windows.FrameworkElement fe, System.Windows.FrameworkContentElement fce, System.Windows.Style oldStyle, System.Windows.Style newStyle) + 0xcd byte
> WindowsBase.dll!System.Windows.DependencyObject.UpdateEffectiveValue(System.Windows.EntryIndex entryIndex, System.Windows.DependencyProperty dp, System.Windows.PropertyMetadata metadata, System.Windows.EffectiveValueEntry oldEntry, ref System.Windows.EffectiveValueEntry newEntry, bool coerceWithDeferredReference, bool coerceWithCurrentValue, System.Windows.OperationType operationType) + 0x757 byte
+ newEntry {System.Windows.EffectiveValueEntry} System.Windows.EffectiveValueEntry
+ _value {System.Windows.Style} object {System.Windows.Style}
+ _value {#FF0000FF} object {System.Windows.Media.SolidColorBrush}
+ _targetType {System.Windows.Controls.TextBlock} System.Type {System.RuntimeType}
> PresentationFramework.dll!System.Windows.FrameworkElement.UpdateStyleProperty() + 0x63 byte
Please note, if you are interested, in a further effort the PropertyIndex shown above (linked to the Foreground DependencyProperty) could be traced back to the:
BamlSchemaContext, a Non-Public Member of the xamlReader in the WpfXamlLoader.LoadBaml, that contains the System.Xaml.IXamlLineInfo.LineNumber
Edit
Here is a starting draft of how to automate the stack trace analysis
[ValueConversion(typeof(Color), typeof(String))]
[Serializable]
public class SolidBrushToColorConverter : IValueConverter
{
protected MethodInfo EffectiveValueEntryValueGetMethod
{
get
{
if (effectiveValueEntryValueGetMethod == null)
{
var effectiveValueEntryType = AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes()).Where(t => t.Name == "EffectiveValueEntry").FirstOrDefault();
if (effectiveValueEntryType == null)
throw new InvalidOperationException();
var effectiveValueEntryValuePropertyInfo = effectiveValueEntryType.GetProperty("Value", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.DeclaredOnly | System.Reflection.BindingFlags.Instance);
if (effectiveValueEntryValuePropertyInfo == null)
throw new InvalidOperationException();
effectiveValueEntryValueGetMethod = effectiveValueEntryValuePropertyInfo.GetGetMethod(nonPublic: true);
if (effectiveValueEntryValueGetMethod == null)
throw new InvalidOperationException();
}
return effectiveValueEntryValueGetMethod;
}
}
protected MethodInfo EffectiveValuesGetMethod
{
get
{
if (effectiveValuesGetMethod == null)
{
var dependencyObjectType = typeof(DependencyObject);
var effectiveValuesPropertyInfo = dependencyObjectType.GetProperty("EffectiveValues", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.DeclaredOnly | System.Reflection.BindingFlags.Instance);
if (effectiveValuesPropertyInfo == null)
throw new InvalidOperationException();
effectiveValuesGetMethod = effectiveValuesPropertyInfo.GetGetMethod(nonPublic: true);
if (effectiveValuesGetMethod == null)
throw new InvalidOperationException();
}
return effectiveValuesGetMethod;
}
}
#region Private fields
private MethodInfo effectiveValueEntryValueGetMethod;
private MethodInfo effectiveValuesGetMethod;
#endregion
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (!(value is Color)) return null;
var result = value.ToString();
if (result.Equals("#FF0000FF")) {
StackTrace st = new StackTrace();
foreach (StackFrame frame in st.GetFrames()) {
if (frame.GetMethod().Name.Equals( "UpdateEffectiveValue" )) {
foreach (ParameterInfo info in frame.GetMethod().GetParameters()) {
Debug.WriteLine ("parameter name " + info.Name
+ ", type " + info.ParameterType.ToString());
if (info.Name.Equals("newEntry")) {
object newEntry = info.GetRealObject(new StreamingContext()); //SET BreakPoint HERE! (to be continued ...)
}
}
}
}
}
return result;
}
here is the view of the Property found from the VS Debugger
WPF inspector has ability to trace styles:
https://wpfinspector.codeplex.com/, see Style tracing section. Is that what are you looking for?
You could use DependencyPropertyHelper.GetValueSource method.
DependencyPropertyHelper is a static class and here is an example how you can use it:
MainWindow.xaml
<Window x:Class="WpfApplication.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
Foreground="Red" Loaded="OnWindowLoaded">
<Window.Resources>
<Style x:Key="TextBlockStyle" TargetType="TextBlock">
<Setter Property="Foreground" Value="Blue"/>
</Style>
</Window.Resources>
<StackPanel>
<TextBlock x:Name="textBlock1"/>
<TextBlock x:Name="textBlock2" Style="{StaticResource TextBlockStyle}"/>
<TextBlock x:Name="textBlock3" Foreground="Green"/>
</StackPanel>
</Window>
MainWindow.xaml.cs
using System.Windows;
using System.Windows.Controls;
namespace WpfApplication
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void OnWindowLoaded(object sender, RoutedEventArgs e)
{
SetValueSource(this.textBlock1);
SetValueSource(this.textBlock2);
SetValueSource(this.textBlock3);
}
private static void SetValueSource(TextBlock textBlock)
{
textBlock.Text = DependencyPropertyHelper.GetValueSource(textBlock, TextBlock.ForegroundProperty).BaseValueSource.ToString();
}
}
}
Output
I think you have written a common style for text block in the Resource dictionary
Just try overriding the style by
<TextBlock.Resources>
<Style TargetType="{x:Type TextBlock}">
</Style>
</TextBlock.Resources>
I'm trying to make a custom converter that inherits from DependencyObject, but it doesn't work:
Converter:
public class BindingConverter : DependencyObject , IValueConverter
{
public object Value
{
get { return (object)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(object), typeof(BindingConverter), new PropertyMetadata(null));
public object Convert(object value, Type targetType, object parameter, Globalization.CultureInfo culture)
{
Debug.Assert(Value != null); //fails
return Value;
}
public object ConvertBack(object value, Type targetType, object parameter, Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Xaml:
<StackPanel x:Name="this">
<!--works-->
<ContentControl Content="{Binding ActualHeight, ElementName=this}"/>
<!--doesn't work-->
<ContentControl>
<Binding>
<Binding.Converter>
<BindingConverter Value="{Binding ActualHeight, ElementName=this}" />
</Binding.Converter>
</Binding>
</ContentControl>
<TextBlock Text="{Binding Animals}"/>
</StackPanel>
Am I missing out anything?
I have some places in my projects where I needed similar functionality. Can't show you exact sample, just an idea:
perhaps you have to inherit from FrameworkElement, not IValueConverter, Something like this:
public class BindingHelper : FrameworkElement
in the BindingHelper class, set Visibility to Collapsed and IsHitTestVisible to false;
to make it working, insert it into visual tree directly. In your example, it should be a child of the StackPanel. So, it will have the same DataContext as other StackPanel children;
then, you can add one ore more dependency properties depending on your needs. For example, you might have single property for the source of data and some different properties which you then will use as converter return values. Handle all changes to the source property in your BindingHelper class and change output properties accordingly;
bind other controls to properties of the BindingHelper class using ElementName syntax
in Silverlight, ActualHeight and ActualWidth properties don't do notifications on property updates. So, binding to them won't work.
Note! ActualHeight property's binding is buggy on binding!
Why you inherit DependencyObject when coding a converter? You should just implement IValueConverter.
Try that,
First add MyConverter by the key of "MyConverterResource" on your resources then,
You can do than on XAML side or on cs side by
//You may do it on XAML side <UserControl.Resources>...
this.Resources.Add("MyConverterResource",new MyConverter());
<TextBlock Text="{Binding ActualHeight,ElementName=this
,Converter=MyConverterResource}"/>
public class MyConverter: IValueConverter
{
public object Convert(object value, Type targetType
, object parameter,Globalization.CultureInfo culture)
{
return "Your Height is:"+Value.toString();
}
}
Hope helps
I created a ControlTemplate for my custom control MyControl.
MyControl derives from System.Windows.Controls.Control and defines the following property public ObservableCollection<MyControl> Children{ get; protected set; }.
To display the nested child controls I am using an ItemsControl (StackPanel) which is surrounded by a GroupBox. If there are no child controls, I want to hide the GroupBox.
Everything works fine on application startup: The group box and child controls are shown if the Children property initially contained at least one element. In the other case it is hidden.
The problem starts when the user adds a child control to an empty collection. The GroupBox's visibility is still collapsed. The same problem occurs when the last child control is removed from the collection. The GroupBox is still visible.
Another symptom is that the HideEmptyEnumerationConverter converter does not get called.
Adding/removing child controls to non empty collections works as expected.
Whats wrong with the following binding? Obviously it works once but does not get updated, although the collection I am binding to is of type ObservableCollection.
<!-- Converter for hiding empty enumerations -->
<Common:HideEmptyEnumerationConverter x:Key="hideEmptyEnumerationConverter"/>
<!--- ... --->
<ControlTemplate TargetType="{x:Type MyControl}">
<!-- ... other stuff that works ... -->
<!-- Child components -->
<GroupBox Header="Children"
Visibility="{Binding RelativeSource={RelativeSource TemplatedParent},
Path=Children, Converter={StaticResource hideEmptyEnumerationConverter}}">
<ItemsControl ItemsSource="{TemplateBinding Children}"/>
</GroupBox>
</ControlTemplate>
.
[ValueConversion(typeof (IEnumerable), typeof (Visibility))]
public class HideEmptyEnumerationConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
int itemCount = ((IEnumerable) value).Cast<object>().Count();
return itemCount == 0 ? Visibility.Collapsed : Visibility.Visible;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
Another, more general question: How do you guys debug bindings? Found this (http://bea.stollnitz.com/blog/?p=52) but still I find it very hard to do.
I am glad for any help or suggestion.
The problem is that your Children property itself never changes, just its content. Since the property value doesn't change, the binding isn't reevaluated. What you need to do is bind to the Count property of the collection. The easiest way you can achieve this is with a DataTrigger in your template:
<ControlTemplate TargetType="{x:Type MyControl}">
<!-- ... other stuff that works ... -->
<!-- Child components -->
<GroupBox x:Name="gb" Header="Children">
<ItemsControl ItemsSource="{TemplateBinding Children}"/>
</GroupBox>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding Path=Children.Count, RelativeSource={RelativeSource TemplatedParent}}"
Value="0">
<Setter TargetName="gb" Property="Visibility" Value="Collapsed" />
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
You need to notify whenever the number of items in your Children property changes. You can do that by implementing INotifyPropertyChanged interface, register to Children collection's CollectionChanged event and raise PropertyChanged from there.
Example:
public class MyControl : Control, INotifyPropertyChanged
{
static MyControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyControl), new FrameworkPropertyMetadata(typeof(MyControl)));
}
public ObservableCollection<UIElement> Children
{
get { return (ObservableCollection<UIElement>)GetValue(ChildrenProperty); }
set { SetValue(ChildrenProperty, value); }
}
// Using a DependencyProperty as the backing store for Children. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ChildrenProperty =
DependencyProperty.Register("Children", typeof(ObservableCollection<UIElement>), typeof(MyControl), new UIPropertyMetadata(0));
public MyControl()
{
Children = new ObservableCollection<UIElement>();
Children.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(Children_CollectionChanged);
}
void Children_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
RaisePropertyChanged("Children");
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(String propertyName)
{
PropertyChangedEventHandler temp = PropertyChanged;
if (temp != null)
{
temp(this, new PropertyChangedEventArgs(propertyName));
}
}
}