locked
Custom Control Code Behind?

    Question

  • I am creating a custom control. The idea is to use 4 sliders to set the RGBA values and a rectangle to display the resulting color. I have created a Dependency Property for the color result so I can bind to it. That is working so far. What I don't understand is how to combine the 4 slider results into a single color value. I know how to do this, just not where to do it. There doesn't seem to be any code behind for custom controls. Where do I implement the logic for my custom controls? What am I missing here? Thanks.
    Tuesday, May 14, 2013 4:09 AM

Answers

  • You could, but there is no advantage and significant disadvantages.

    You could hook up slider events, but there is no advantage and significant disadvantages.

    It's generally a good idea to separate the code from the Xaml as much as possible. If the code doesn't depend on having a slider then the Xaml can be customized to use other controls.

    For Mark's scenario the important part is the link between the A,R,G,B and ARGB properties. I'd set each of these up as a dependency property and set up change handlers to keep them in sync.

    The Xaml could then bind sliders to A,R,G, and B and changing these would in turn update ARGB. The Xaml could also (or instead) bind A,R,G,B, and ARGB to TextBoxes, color fields, etc.

    If the code draws the changes from slider events rather than from data binding then it will only work with sliders.

    --Rob

     

    • Marked as answer by MarkFlamer Friday, May 17, 2013 12:02 AM
    Wednesday, May 15, 2013 7:44 PM
    Owner

All replies

  • Custom controls have a class which inherits from Control. Their dependency properties and other code behind go in that class.

    See XAML user and custom controls sample for an example.

    --Rob

    Tuesday, May 14, 2013 4:50 AM
    Owner
  • That's the sample I used to get the custom control as far as I have so far. The custom control example provided (ImageWithLabelControl) has only the dependency property defined, no example of any other mechanics going on behind the control. Unless I'm missing something. Thanks Rob.
    Tuesday, May 14, 2013 5:36 AM
  • You can put whatever other mechanics you'd like in the ImageWithLabelControl class.

    You can add a property for your combined color value in that class and set it when the properties for your RGBA values change.

    --Rob

    Tuesday, May 14, 2013 5:48 AM
    Owner
  • In your custom control define event handlers for the ValueChanged event for the sliders. Adjust the value of your RGBA property in the handlers.
    Tuesday, May 14, 2013 2:19 PM
  • I can't seem to add any events to the controls in my Generic.xaml file. There doesn't seem to be any connection between the custom control class and xaml file. Why does the user control sample have an associated xaml file but the custom control sample only has Generic.xaml. Do I have to use dependency properties for each color channel to get a connection to the xaml?  I'm missing something fundamental here. Thanks for being patient with me, this is entirely different that the normal work I do. 
    • Edited by MarkFlamer Wednesday, May 15, 2013 6:40 AM
    Wednesday, May 15, 2013 6:38 AM
  • As I understand the problem:

    • You have four sliders in your custom control that are used to adjust a single RGBA value.
    • You want to expose the RGBA value as a dependency property of the control.

    Here's what I would do:

    • Create a dependency property that exposes the RGBA value of the custom control so that consumers of the control can bind to that property.
    • Create a delegate and an event for the changing of the RGBA value. This way consumers of your control can create event handlers around the changing of the RGBA value.
    • Add event handlers for the ValueChanged event of the sliders in the custom control. In the event handlers set the value of the RGBA dependency property to reflect the value of the sliders.

    Here's some links to get you going.

    http://msdn.microsoft.com/en-us/library/windows/apps/Hh700353.aspx

    http://msdn.microsoft.com/en-us/library/windows/apps/hh441569.aspx

    Wednesday, May 15, 2013 2:38 PM
  • Jim, that describes my situation. I have everything done except the last bullet.

    "Add event handlers for the ValueChanged event of the sliders in the custom control. In the event handlers set the value of the RGBA dependency property to reflect the value of the sliders."

    I started my custom control from within an existing project using the Add-Templated Control menu item. This command added 3 files to my project.

    -ColorPicker.h

    -ColorPicker.cpp

    -Generic.xaml

    I have added my sliders to Generic.xaml and they display correctly when I use the ColorPicker control on one of my project pages. But, If I try to add an event handler to one of the sliders in Generic.xaml it is not possible because it does not have a code behind file. Where would I define these event handlers? 

    • Proposed as answer by Jesse Jiang Thursday, May 16, 2013 2:49 AM
    Wednesday, May 15, 2013 3:48 PM
  • First: don't hook up event handlers for this. Use databinding. That way you can add textboxes to the sliders, etc.

    Second: you can hook up event handlers in code. Find the templates controls in OnApplyTemplate and add event handlers programmatically. I think the custom

    control sample demos this, but I can't

    check easily from my phone.

    Wednesday, May 15, 2013 4:05 PM
    Owner
  • First: don't hook up event handlers for this. Use databinding. That way you can add textboxes to the sliders, etc.

    Can't he do both? It seems like there is a need to get the slider value directly when it changes in addition to possibly binding the value with a content provider.
    Wednesday, May 15, 2013 4:35 PM
  • You could, but there is no advantage and significant disadvantages.

    You could hook up slider events, but there is no advantage and significant disadvantages.

    It's generally a good idea to separate the code from the Xaml as much as possible. If the code doesn't depend on having a slider then the Xaml can be customized to use other controls.

    For Mark's scenario the important part is the link between the A,R,G,B and ARGB properties. I'd set each of these up as a dependency property and set up change handlers to keep them in sync.

    The Xaml could then bind sliders to A,R,G, and B and changing these would in turn update ARGB. The Xaml could also (or instead) bind A,R,G,B, and ARGB to TextBoxes, color fields, etc.

    If the code draws the changes from slider events rather than from data binding then it will only work with sliders.

    --Rob

     

    • Marked as answer by MarkFlamer Friday, May 17, 2013 12:02 AM
    Wednesday, May 15, 2013 7:44 PM
    Owner
  • Some great suggestions Rob. I'm realizing how I can use these ideas in some custom controls I'm working on. Although I've created dependency properties that can be bound to, I never though of using binding to eliminate having to use event handlers.
    Wednesday, May 15, 2013 8:17 PM
  • OK, I think I'm getting this now. Create dependency properties for any data transfer between the class and the Xaml. In this case 5 total, 1 for the color to bind to a rectangle for a color "preview" on the control and 4 for the RGBA channels. This way the Xaml(view?) and class(controller?) are decoupled. I'll try this tonight. Thanks for all the help guys.
    • Proposed as answer by Jesse Jiang Thursday, May 16, 2013 2:49 AM
    Wednesday, May 15, 2013 9:06 PM
  • I think I'm really close now. Still cant quite get it all to work. It builds and runs but the OnRedChanged() never gets called. I'm posting the code here if either of you can take a quick look. Thanks again for all the help.

    Here is Generic.xaml

    <ResourceDictionary    
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:PhysInk">
    
        <Style TargetType="local:ColorPicker">       
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="local:ColorPicker">                   
                        <Grid Height="Auto" Width="Auto" MinWidth="160" MinHeight="48">
                            <Grid.RowDefinitions>
                                <RowDefinition Height="Auto"/>
                            </Grid.RowDefinitions>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="3*"/>
                                <ColumnDefinition Width="2*"/>
                            </Grid.ColumnDefinitions>
    
                            <Rectangle Fill="{TemplateBinding ColorPicked}" Grid.Column="1" Width="Auto" Height="Auto" />
                            <StackPanel Grid.Column="0" VerticalAlignment="Top">
                                <Slider x:Name="SliderR" Value="{TemplateBinding ChanelRed}" Minimum="0" Maximum="255" BorderBrush="LightGray" BorderThickness="0" Margin="1,0" TickPlacement="None" TickFrequency="1" StepFrequency="1" Padding="0,-4,0,0" Height="24" >
                                    <Slider.Foreground>
                                        <LinearGradientBrush EndPoint="1,1" StartPoint="0.5,0">
                                            <GradientStop Color="Black"/>
                                            <GradientStop Color="Red" Offset="1"/>
                                        </LinearGradientBrush>
                                    </Slider.Foreground>
                                    <Slider.Background>
                                        <LinearGradientBrush EndPoint="1,0" StartPoint="0,0">
                                            <GradientStop Color="Black"/>
                                            <GradientStop Color="Red" Offset="1"/>
                                        </LinearGradientBrush>
                                    </Slider.Background>
                                </Slider>
                                <Slider x:Name="SliderG" Minimum="0" Maximum="255" Canvas.Left="3"                         
                        		BorderBrush="LightGray" BorderThickness="0" Margin="1,0" Height="24" Padding="0,-4,0,0" TickPlacement="None">
                                     <Slider.Foreground>
                                        <LinearGradientBrush EndPoint="1,0" StartPoint="0,0">
                                            <GradientStop Color="Black"/>
                                            <GradientStop Color="Lime" Offset="1"/>
                                        </LinearGradientBrush>
                                    </Slider.Foreground>
                                    <Slider.Background>
                                        <LinearGradientBrush EndPoint="1,0" StartPoint="0,0">
                                            <GradientStop Color="Black"/>
                                            <GradientStop Color="Lime" Offset="1"/>
                                        </LinearGradientBrush>
                                    </Slider.Background>                              
                                </Slider>
                                <Slider x:Name="SliderB" Minimum="0" Maximum="255" Canvas.Top="14" Canvas.Left="3"                       
                        		BorderBrush="LightGray" BorderThickness="2,2,2,1" Margin="1,0" TickFrequency="1" TickPlacement="None" Height="24" Padding="0,-4,0,0">
                                    <Slider.Foreground>
                                        <LinearGradientBrush EndPoint="1,0" StartPoint="0,0">
                                            <GradientStop Color="Black"/>
                                            <GradientStop Color="Blue" Offset="1"/>
                                        </LinearGradientBrush>
                                    </Slider.Foreground>
                                    <Slider.Background>
                                        <LinearGradientBrush EndPoint="1,0" StartPoint="0,0">
                                            <GradientStop Color="Black"/>
                                            <GradientStop Color="Blue" Offset="1"/>
                                        </LinearGradientBrush>
                                    </Slider.Background>
                                </Slider>
                                <Slider x:Name="SliderA" Minimum="0" Maximum="255" Canvas.Left="3" Canvas.Top="28"                        
                        		BorderBrush="LightGray" BorderThickness="2" Margin="1,0" TickPlacement="None" TickFrequency="1" Height="24" Padding="0,-4,0,0">
                                    <Slider.Background>
                                        <LinearGradientBrush EndPoint="1,0" StartPoint="0,0">
                                            <GradientStop Color="Black"/>
                                            <GradientStop Color="White" Offset="1"/>
                                        </LinearGradientBrush>
                                    </Slider.Background>
                                    <Slider.Foreground>
                                        <LinearGradientBrush EndPoint="1,0" StartPoint="0,0">
                                            <GradientStop Color="Black"/>
                                            <GradientStop Color="White" Offset="1"/>
                                        </LinearGradientBrush>
                                    </Slider.Foreground>
                                </Slider>
                            </StackPanel>                       
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </ResourceDictionary>

    //
    // ColorPicker.h
    // Declaration of the ColorPicker class.
    //
    
    #pragma once
    
    #include "pch.h"
    
    using namespace Windows::UI::Xaml::Controls;
    using namespace Windows::UI::Xaml::Interop;
    using namespace Windows::UI::Xaml;
    using namespace Windows::UI::Xaml::Media;
    using namespace Platform;
    
    namespace PhysInk
    {
    [Windows::Foundation::Metadata::WebHostHidden]
    public ref class ColorPicker sealed : public Windows::UI::Xaml::Controls::Control
    {
    private:
        	static DependencyProperty^ _ColorPickedProperty;
    		static DependencyProperty^ _ChanelRedProperty;
    		static DependencyProperty^ _ChanelGreenProperty;
    		static DependencyProperty^ _ChanelBlueProperty;
    		static DependencyProperty^ _ChanelAlphaProperty;
    
    		static void OnRedChanged(DependencyObject^ d, DependencyPropertyChangedEventArgs^ e);
    
    public:
    	ColorPicker();
    
    	static property DependencyProperty^ ColorPickedProperty
        		{
        			DependencyProperty^ get() { return _ColorPickedProperty; }
        		}
    
    	static property DependencyProperty^ ChanelRedProperty
        		{
        			DependencyProperty^ get() { return _ChanelRedProperty; }
        		}
    
    	static property DependencyProperty^ ChanelGreenProperty
        		{
        			DependencyProperty^ get() { return _ChanelGreenProperty; }
        		}
    
    	static property DependencyProperty^ ChanelBlueProperty
        		{
        			DependencyProperty^ get() { return _ChanelBlueProperty; }
        		}
    
    	static property DependencyProperty^ ChanelAlphaProperty
        		{
        			DependencyProperty^ get() { return _ChanelAlphaProperty; }
        		}
    
    
    
    	property SolidColorBrush^ ColorPicked
        		{
        			SolidColorBrush^ get() { return (SolidColorBrush^)GetValue(ColorPickedProperty); }
        			void set(SolidColorBrush^ value) { SetValue(ColorPickedProperty, value); }
        		}
    
    	property double ChanelRed
        		{
    				double get() { return static_cast<double>(GetValue(ChanelRedProperty)); }
        			void set(double value) 
    				{
    					Platform::Object^ obj = value;
    					SetValue(ChanelRedProperty, obj); 
    				}
        		}
    
    	property uint8 ChanelGreen
        		{
    				uint8 get() { return static_cast<uint8>(GetValue(ChanelGreenProperty)); }
        			void set(uint8 value) 
    				{
    					Platform::Object^ obj = value;
    					SetValue(ChanelGreenProperty, obj); 
    				}
        		}
    
    	property uint8 ChanelBlue
        		{
    				uint8 get() { return static_cast<uint8>(GetValue(ChanelBlueProperty)); }
        			void set(uint8 value) 
    				{
    					Platform::Object^ obj = value;
    					SetValue(ChanelBlueProperty, obj); 
    				}
        		}
    
    	property uint8 ChanelAlpha
        		{
    				uint8 get() { return static_cast<uint8>(GetValue(ChanelAlphaProperty)); }
        			void set(uint8 value) 
    				{
    					Platform::Object^ obj = value;
    					SetValue(ChanelAlphaProperty, obj); 
    				}
        		}
    		
    	
    	
    
    };
    }
    

    //
    // ColorPicker.cpp
    // Implementation of the ColorPicker class.
    //
    
    #include "pch.h"
    #include "ColorPicker.h"
    
    using namespace PhysInk;
    
    using namespace default;
    using namespace Platform;
    using namespace Windows::Foundation;
    using namespace Windows::Foundation::Collections;
    using namespace Windows::UI::Xaml;
    using namespace Windows::UI::Xaml::Controls;
    using namespace Windows::UI::Xaml::Data;
    using namespace Windows::UI::Xaml::Documents;
    using namespace Windows::UI::Xaml::Input;
    using namespace Windows::UI::Xaml::Interop;
    using namespace Windows::UI::Xaml::Media;
    
    // The Templated Control item template is documented at http://go.microsoft.com/fwlink/?LinkId=234235
    
    ColorPicker::ColorPicker()
    {
    	DefaultStyleKey = "PhysInk.ColorPicker";
    }
    
    DependencyProperty^ ColorPicker::_ColorPickedProperty = DependencyProperty::Register("ColorPicked", SolidColorBrush::typeid, ColorPicker::typeid, nullptr);
    DependencyProperty^ ColorPicker::_ChanelRedProperty = DependencyProperty::Register("ChanelRed", double::typeid, ColorPicker::typeid, 
    	ref new PropertyMetadata(nullptr, ref new PropertyChangedCallback(&ColorPicker::OnRedChanged)));
    DependencyProperty^ ColorPicker::_ChanelGreenProperty = DependencyProperty::Register("ChanelGreen", uint8::typeid, ColorPicker::typeid, nullptr);
    DependencyProperty^ ColorPicker::_ChanelBlueProperty = DependencyProperty::Register("ChanelBlue", uint8::typeid, ColorPicker::typeid, nullptr);
    DependencyProperty^ ColorPicker::_ChanelAlphaProperty = DependencyProperty::Register("ChanelAlpha", uint8::typeid, ColorPicker::typeid, nullptr);
    
    
    
    void ColorPicker::OnRedChanged(DependencyObject^ d, DependencyPropertyChangedEventArgs^ e)
    {
        ColorPicker^ cp = (ColorPicker^)d;
        uint8 newRed = (uint8)(e->NewValue);
    	Windows::UI::Color newColor = cp->ColorPicked->Color;
        newColor.R = newRed;
    	cp->ColorPicked->Color = newColor;
    }
    

     
    Thursday, May 16, 2013 6:01 AM
  • I realize it might be a little much to expect someone to look through my code. Does anyone know of any other examples using dependency properties with call backs? Thanks.
    Thursday, May 16, 2013 6:23 PM
  • I'll take a shot. You're binding the Value property of the slider to your Chanel dependency property but you also need to have the binding go the other direction (which would cause the change handler to be called). As I understand it TemplateBinding is one-way. To do a two way binding you need to use this syntax:

    Value="{Binding Path=ChanelRed, RelativeSource={RelativeSource TemplatedParent},Mode=TwoWay}"

    http://msdn.microsoft.com/en-us/library/ms742882.aspx

    http://stackoverflow.com/questions/5913176/in-wpf-why-doesnt-templatebinding-work-where-binding-does

    Thursday, May 16, 2013 8:13 PM
  • That's it! Thanks again Jim.
    Friday, May 17, 2013 12:02 AM