locked
2 проблемы с UserControl RRS feed

  • Вопрос

  • Доброго времени суток!

    Захотел я освоить нелегкое дело контролописца. Решил начать спростого. Задача такая. Запилить контрол состоянии жизни сервера. Представляет он из себя тупо квадрат. Есть у него DependecyProperty - Status (4 состояния из Enum: Disabled, Offline, Online, Unknown) . Вот хочу сделать чтобы квадрат менял цвета в зависимости это этой проперти на соответственно (серый, красный, зеленый, прозрачный). Вот замл контрола. Делал по инструкции на мсдн.

    <UserControl
    	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    	xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    	xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    	mc:Ignorable="d"
    	x:Class="MyControls.ServerStatusControl"
    	d:DesignWidth="44" d:DesignHeight="44"
    	FontFamily="{StaticResource PhoneFontFamilyNormal}"
    	FontSize="{StaticResource PhoneFontSizeNormal}"
    	Foreground="{StaticResource PhoneForegroundBrush}">
    
    	<Grid x:Name="LayoutRoot">
    		<VisualStateManager.VisualStateGroups>
    			<VisualStateGroup x:Name="VisualStateGroup">
    				<VisualStateGroup.Transitions>
    					<VisualTransition GeneratedDuration="0:0:0.3" To="StatusOffline">
    						<Storyboard>
    							<ColorAnimation Duration="0:0:0.3" To="#FFA20025" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" Storyboard.TargetName="rectangle" d:IsOptimized="True"/>
    						</Storyboard>
    					</VisualTransition>
    					<VisualTransition GeneratedDuration="0:0:0.3" To="StatusOnline">
    						<Storyboard>
    							<ColorAnimation Duration="0:0:0.3" To="#FF008A00" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" Storyboard.TargetName="rectangle" d:IsOptimized="True"/>
    						</Storyboard>
    					</VisualTransition>
    					<VisualTransition GeneratedDuration="0:0:0.3" To="StatusDisabled">
    						<Storyboard>
    							<ColorAnimation Duration="0:0:0.3" To="#FF4E4E4E" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" Storyboard.TargetName="rectangle" d:IsOptimized="True"/>
    						</Storyboard>
    					</VisualTransition>
    					<VisualTransition GeneratedDuration="0:0:0.3" To="StatusUnknown"/>
    				</VisualStateGroup.Transitions>
    				<VisualState x:Name="StatusUnknown"/>
    				<VisualState x:Name="StatusDisabled"/>
    				<VisualState x:Name="StatusOnline"/>
    				<VisualState x:Name="StatusOffline"/>
    			</VisualStateGroup>
    		</VisualStateManager.VisualStateGroups>
    		<Rectangle x:Name="rectangle" Margin="0" Stroke="Black" StrokeThickness="0" Fill="#00FFFFFF" />
    	</Grid>
    </UserControl>


    А вот что в cs к нему. Кишочки так сказать:

    namespace MyControls
    {
        public enum ServerStatus
        {
            Disabled,
            Offline,
            Online,
            Unknown
        }
    
    	public partial class ServerStatusControl : UserControl
    	{
    		public ServerStatusControl()
    		{
    			// Required to initialize variables
    			InitializeComponent();
    		}
    
            // Status Dependency Property
            public ServerStatus Status
            {
                get { return (ServerStatus)GetValue(StatusProperty); }
                set { SetValue(StatusProperty, value); }
            }
    
            public static readonly DependencyProperty StatusProperty =
                DependencyProperty.Register("Status", typeof(ServerStatus), typeof(UserControl), new PropertyMetadata(ServerStatus.Unknown, new PropertyChangedCallback(OnStatusChanged)));
    		
            static void OnStatusChanged(object sender, DependencyPropertyChangedEventArgs args)
            {
                var control = sender as ServerStatusControl;
                if (control != null)
                {
                    switch(control.Status)
                    {
                        case ServerStatus.Disabled:
                            VisualStateManager.GoToState(control, "StatusDisabled", true);
                            break;
                        case ServerStatus.Offline:
                            VisualStateManager.GoToState(control, "StatusOffline", true);
                            break;
                        case ServerStatus.Online:
                            VisualStateManager.GoToState(control, "StatusOnline", true);
                            break;
                        case ServerStatus.Unknown:
                            VisualStateManager.GoToState(control, "StatusUnknown", true);
                            break;
                    }               
                }
            }		
    	}
    }

    Теперь добавляю на страницу. (В одном солюшене находятся проект контрола и приложение винфон, добавлена ссылка на проект контрола)

    xmlns:mycontrols="clr-namespace:MyControls;assembly=ServerStatusControl"
        ......
        shell:SystemTray.IsVisible="True">
    
    ....
                            <mycontrols:ServerStatusControl Name="ssc_Status" Status="Offline" Width="40" Height="40"/>
    


    Тут оказывается что стейт не работает. Глянул в дебагере колбек проперти, там реально доходит до 

    VisualStateManager.GoToState(control, "StatusOffline", true);

    Я ожидаю, что после этого контрол станет красным, ан нет. Так и остается прозрачным (как по дефолту).

    Вопрос номер 2:

    Не могу достать мой контрол из кода. Пробовал:

    Name="ssc_Status"
    x:Name="ssc_Status"

    Все равно в коде при компиляции вижу это

    Error 1 The name 'ssc_Status' does not exist in the current context \Projects\Test001\Test001\MainPage.xaml.cs 24 13 Test001

    Вот такие дела. Надеюсь на вашу помощь (:


    • Изменено Alexey Gurin 19 января 2014 г. 17:31
    19 января 2014 г. 17:30

Ответы

  • Здравствуйте.

    Ссылка на проект -http://sdrv.ms/1fNy50P

    Попробуйте так:

    MainPage.xaml:

     <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
                <StackPanel>
                    <Button Name="button1"
                            Content="Change status"
                            Click="button1_Click">
                    </Button>
                    <TextBlock>
                        <Run Text="Current value: "/>
                        <LineBreak/>
                        <Run Text="{Binding Status}"/>
                    </TextBlock>
                    
                    <localControls:ServerStatusControl x:Name="myStatusControl"
                                                       ServerStatus="{Binding ServerStatus}"/>
                </StackPanel>
            </Grid>

    MainPage.xaml.cs:

    public partial class MainPage : PhoneApplicationPage, INotifyPropertyChanged
        {
            private Random rand = new Random();
            private ServerStatus serverStatus;
            private string status;
    
            public string Status
            {
                get { return status; }
                set
                {
                    status = value;
                    this.RaiseProperty();
                }
            }
    
            public ServerStatus ServerStatus
            {
                get { return serverStatus; }
                set
                {
                    serverStatus = value;
                    this.Status = this.serverStatus.ToString();
                    this.RaiseProperty();
                }
            }
    
            // Constructor
            public MainPage()
            {
                InitializeComponent();
                this.DataContext = this;
    
            }
    
            public event PropertyChangedEventHandler PropertyChanged;
    
            public void RaiseProperty([CallerMemberName] string property = "")
            {
                var handler = this.PropertyChanged;
                if (handler != null)
                {
                    handler.Invoke(this, new PropertyChangedEventArgs(property));
                }
            }
    
            private void button1_Click(object sender, RoutedEventArgs e)
            {
                var value = this.rand.Next(3);
                this.ServerStatus = (PhoneApp1.ServerStatus)value;
            }

    ServerStatusControl.xaml:

    <UserControl x:Class="PhoneApp1.Controls.ServerStatusControl"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        FontFamily="{StaticResource PhoneFontFamilyNormal}"
        FontSize="{StaticResource PhoneFontSizeNormal}"
        Foreground="{StaticResource PhoneForegroundBrush}"
        d:DesignHeight="480" d:DesignWidth="480">
    
        <Grid x:Name="LayoutRoot">
            <!--Можно и так, вместо Rectangle-->
            <!--<Grid.Background>
                <SolidColorBrush x:Name="brush" Color="Red"/>
            </Grid.Background>-->
            
            <VisualStateManager.VisualStateGroups>
                <VisualStateGroup x:Name="VisualStateGroup">
                    <VisualState x:Name="StatusUnknown">
                        <Storyboard>
                            <ColorAnimation Duration="0:0:0.3" To="#FF02A4F8" Storyboard.TargetProperty="Color" Storyboard.TargetName="brush" d:IsOptimized="True"/>
                        </Storyboard>
                    </VisualState>
                    <VisualState x:Name="StatusDisabled">
                        <Storyboard>
                            <ColorAnimation Duration="0:0:0.3" To="#FF4E4E4E" Storyboard.TargetProperty="Color" Storyboard.TargetName="brush" d:IsOptimized="True"/>
                        </Storyboard>
                    </VisualState>
                    <VisualState x:Name="StatusOnline">
                        <Storyboard>
                            <ColorAnimation Duration="0:0:0.3" To="#FF008A00" Storyboard.TargetProperty="Color" Storyboard.TargetName="brush" d:IsOptimized="True"/>
                        </Storyboard>
                    </VisualState>
                    <VisualState x:Name="StatusOffline">
                        <Storyboard>
                            <ColorAnimation Duration="0:0:0.3" To="#FFA20025" Storyboard.TargetProperty="Color" Storyboard.TargetName="brush" d:IsOptimized="True"/>
                        </Storyboard>
                    </VisualState>
                </VisualStateGroup>
            </VisualStateManager.VisualStateGroups>
    
            <Rectangle x:Name="rectangle" Margin="0" 
                       Stroke="Black" 
                       StrokeThickness="0"
                       HorizontalAlignment="Stretch"
                       VerticalAlignment="Stretch"
                       Height="100"
                       Width="100">
                <Rectangle.Fill>
                    <SolidColorBrush x:Name="brush" Color="Red"/>
                </Rectangle.Fill>
            </Rectangle>
        </Grid>
    </UserControl>

    ServerStatusControl.xaml.cs:

    public partial class ServerStatusControl : UserControl
        {
            public ServerStatus ServerStatus
            {
                get { return (ServerStatus)GetValue(ServerStatusProperty); }
                set { SetValue(ServerStatusProperty, value); }
            }
    
            // Using a DependencyProperty as the backing store for ServerStatus.  This enables animation, styling, binding, etc...
            public static readonly DependencyProperty ServerStatusProperty =
                DependencyProperty.Register("ServerStatus", typeof(ServerStatus), typeof(ServerStatusControl), new PropertyMetadata(ServerStatus.Unknown, OnServerStatusChanged));
    
            public static void OnServerStatusChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                var control = d as ServerStatusControl;
                if (control != null)
                {
                    var h = control.Height;
                    var w = control.Width;
                    var ha = control.ActualHeight;
                    var wa = control.ActualWidth;
    
                    switch (control.ServerStatus)
                    {
                        case ServerStatus.Disabled:
                            VisualStateManager.GoToState(control, "StatusDisabled", true);
                            break;
                        case ServerStatus.Offline:
                            VisualStateManager.GoToState(control, "StatusOffline", true);
                            break;
                        case ServerStatus.Online:
                            VisualStateManager.GoToState(control, "StatusOnline", true);
                            break;
                        case ServerStatus.Unknown:
                            VisualStateManager.GoToState(control, "StatusUnknown", true);
                            break;
                    }
                }
                
            }
            
            public ServerStatusControl()
            {
                InitializeComponent();
            }
        }

    • Помечено в качестве ответа Alexey Gurin 20 января 2014 г. 10:25
    19 января 2014 г. 20:05

Все ответы

  • Здравствуйте.

    Ссылка на проект -http://sdrv.ms/1fNy50P

    Попробуйте так:

    MainPage.xaml:

     <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
                <StackPanel>
                    <Button Name="button1"
                            Content="Change status"
                            Click="button1_Click">
                    </Button>
                    <TextBlock>
                        <Run Text="Current value: "/>
                        <LineBreak/>
                        <Run Text="{Binding Status}"/>
                    </TextBlock>
                    
                    <localControls:ServerStatusControl x:Name="myStatusControl"
                                                       ServerStatus="{Binding ServerStatus}"/>
                </StackPanel>
            </Grid>

    MainPage.xaml.cs:

    public partial class MainPage : PhoneApplicationPage, INotifyPropertyChanged
        {
            private Random rand = new Random();
            private ServerStatus serverStatus;
            private string status;
    
            public string Status
            {
                get { return status; }
                set
                {
                    status = value;
                    this.RaiseProperty();
                }
            }
    
            public ServerStatus ServerStatus
            {
                get { return serverStatus; }
                set
                {
                    serverStatus = value;
                    this.Status = this.serverStatus.ToString();
                    this.RaiseProperty();
                }
            }
    
            // Constructor
            public MainPage()
            {
                InitializeComponent();
                this.DataContext = this;
    
            }
    
            public event PropertyChangedEventHandler PropertyChanged;
    
            public void RaiseProperty([CallerMemberName] string property = "")
            {
                var handler = this.PropertyChanged;
                if (handler != null)
                {
                    handler.Invoke(this, new PropertyChangedEventArgs(property));
                }
            }
    
            private void button1_Click(object sender, RoutedEventArgs e)
            {
                var value = this.rand.Next(3);
                this.ServerStatus = (PhoneApp1.ServerStatus)value;
            }

    ServerStatusControl.xaml:

    <UserControl x:Class="PhoneApp1.Controls.ServerStatusControl"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        FontFamily="{StaticResource PhoneFontFamilyNormal}"
        FontSize="{StaticResource PhoneFontSizeNormal}"
        Foreground="{StaticResource PhoneForegroundBrush}"
        d:DesignHeight="480" d:DesignWidth="480">
    
        <Grid x:Name="LayoutRoot">
            <!--Можно и так, вместо Rectangle-->
            <!--<Grid.Background>
                <SolidColorBrush x:Name="brush" Color="Red"/>
            </Grid.Background>-->
            
            <VisualStateManager.VisualStateGroups>
                <VisualStateGroup x:Name="VisualStateGroup">
                    <VisualState x:Name="StatusUnknown">
                        <Storyboard>
                            <ColorAnimation Duration="0:0:0.3" To="#FF02A4F8" Storyboard.TargetProperty="Color" Storyboard.TargetName="brush" d:IsOptimized="True"/>
                        </Storyboard>
                    </VisualState>
                    <VisualState x:Name="StatusDisabled">
                        <Storyboard>
                            <ColorAnimation Duration="0:0:0.3" To="#FF4E4E4E" Storyboard.TargetProperty="Color" Storyboard.TargetName="brush" d:IsOptimized="True"/>
                        </Storyboard>
                    </VisualState>
                    <VisualState x:Name="StatusOnline">
                        <Storyboard>
                            <ColorAnimation Duration="0:0:0.3" To="#FF008A00" Storyboard.TargetProperty="Color" Storyboard.TargetName="brush" d:IsOptimized="True"/>
                        </Storyboard>
                    </VisualState>
                    <VisualState x:Name="StatusOffline">
                        <Storyboard>
                            <ColorAnimation Duration="0:0:0.3" To="#FFA20025" Storyboard.TargetProperty="Color" Storyboard.TargetName="brush" d:IsOptimized="True"/>
                        </Storyboard>
                    </VisualState>
                </VisualStateGroup>
            </VisualStateManager.VisualStateGroups>
    
            <Rectangle x:Name="rectangle" Margin="0" 
                       Stroke="Black" 
                       StrokeThickness="0"
                       HorizontalAlignment="Stretch"
                       VerticalAlignment="Stretch"
                       Height="100"
                       Width="100">
                <Rectangle.Fill>
                    <SolidColorBrush x:Name="brush" Color="Red"/>
                </Rectangle.Fill>
            </Rectangle>
        </Grid>
    </UserControl>

    ServerStatusControl.xaml.cs:

    public partial class ServerStatusControl : UserControl
        {
            public ServerStatus ServerStatus
            {
                get { return (ServerStatus)GetValue(ServerStatusProperty); }
                set { SetValue(ServerStatusProperty, value); }
            }
    
            // Using a DependencyProperty as the backing store for ServerStatus.  This enables animation, styling, binding, etc...
            public static readonly DependencyProperty ServerStatusProperty =
                DependencyProperty.Register("ServerStatus", typeof(ServerStatus), typeof(ServerStatusControl), new PropertyMetadata(ServerStatus.Unknown, OnServerStatusChanged));
    
            public static void OnServerStatusChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                var control = d as ServerStatusControl;
                if (control != null)
                {
                    var h = control.Height;
                    var w = control.Width;
                    var ha = control.ActualHeight;
                    var wa = control.ActualWidth;
    
                    switch (control.ServerStatus)
                    {
                        case ServerStatus.Disabled:
                            VisualStateManager.GoToState(control, "StatusDisabled", true);
                            break;
                        case ServerStatus.Offline:
                            VisualStateManager.GoToState(control, "StatusOffline", true);
                            break;
                        case ServerStatus.Online:
                            VisualStateManager.GoToState(control, "StatusOnline", true);
                            break;
                        case ServerStatus.Unknown:
                            VisualStateManager.GoToState(control, "StatusUnknown", true);
                            break;
                    }
                }
                
            }
            
            public ServerStatusControl()
            {
                InitializeComponent();
            }
        }

    • Помечено в качестве ответа Alexey Gurin 20 января 2014 г. 10:25
    19 января 2014 г. 20:05
  • Спасибо. Проблема была в том, что анимации почему то должны быть внутри <VisualState>, а не как у меня отдельно в <VisualState.Transitions>. Узнать бы только теперь в чем разница, почему так происходит? По поводу просто Grid тоже верно, просто изначально планировался контрол со структурой посложнее. Потом передумал и все удалил, а rect оставил. После этого изменения и имя почему то заработало.. 
    • Изменено Alexey Gurin 20 января 2014 г. 10:30
    20 января 2014 г. 10:28