Adding new visual state to CommonStates in inherited controls












0















I'm inheriting DatePicker in order to add IsReadOnly and InputRequired behaviour. have made corresponding visual states for both in CommonStates. The reason I have put it in CommonStates, is that they are both mutually exclusive with the Normal state (I checked TextBox and it the same implementation for IsReadOnly).



The problem I encounter is that even if I apply the InputRequired state in OnApplyTemplate, it does not actually update the appearance. It does however after a MouseEnter event, leading my to conclude that it is only on the initial construction/rendering.



From looking into the source code of Control I can see that in OnPostApplyTemplate, it calls an internal virtual UpdateVisualState method, which is overridden in DatePicker, and which applies the "Normal" state, meaning that my visual state update in OnApplyTemplate is overwritten by Control.



As a workaround I can update the visual state in the Loaded event, and I can also make it a separate VisualStateGroup, and have some more complex logic in the code to determine wether the apply the visual state (e.g. inputrequired should not be applied with disabled/readonly), but both of these feel a little bit hacked.



So my questions is: is there a correct way to inject new visual states in the CommonStates of a control in this way? Or is the best way to listen for the Loaded event and update visual state here (and are there any gotchas in this case)?



Visual states:



    <VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"/>
<VisualState x:Name="MouseOver">
<Storyboard>
<ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetName="Background" Storyboard.TargetProperty="Fill">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource CommonHoverBackgroundColor}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetName="Background" Storyboard.TargetProperty="Stroke">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource CommonHoverBorderColor}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="ReadOnly">
<Storyboard>
<DoubleAnimation To="1" Duration="0" Storyboard.TargetName="ReadOnlyVisual" Storyboard.TargetProperty="Opacity"/>
</Storyboard>
</VisualState>
<VisualState x:Name="InputRequired">
<Storyboard>
<ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetName="Background" Storyboard.TargetProperty="Fill">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource CommonRequiredBackgroundColor}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled">
<Storyboard>
<DoubleAnimation To="1" Duration="0" Storyboard.TargetName="PART_DisabledVisual" Storyboard.TargetProperty="Opacity"/>
</Storyboard>
</VisualState>
</VisualStateGroup>


UpdateVisualState in codebehind



private void UpdateVisualState(bool useTransitions = true)
{
// Common states
if (IsEnabled == false)
{
VisualStateManager.GoToState(this, "Disabled", useTransitions);
}
else if (IsMouseOver)
{
VisualStateManager.GoToState(this, "MouseOver", useTransitions);
}
else if (IsReadOnly)
{
VisualStateManager.GoToState(this, "ReadOnly", useTransitions);
}
if (CheckInputRequired())
{
VisualStateManager.GoToState(this, "Required", useTransitions);
}
else
{
VisualStateManager.GoToState(this, "Normal", useTransitions);
}

if (IsKeyboardFocusWithin)
{
VisualStateManager.GoToState(this, "Focused", useTransitions);
}
else
{
VisualStateManager.GoToState(this, "Unfocused", useTransitions);
}

// Clearable states
if (IsReadOnly == false && AllowNull && SelectedDate != null)
{
VisualStateManager.GoToState(this, "Clearable", useTransitions);
}
else
{
VisualStateManager.GoToState(this, "Unclearable", useTransitions);
}

// Watermark states
if (IsKeyboardFocusWithin)
{
// When control has keyboard focus, always hide watermark.
VisualStateManager.GoToState(this, "Unwatermarked", useTransitions);
}
else if (string.IsNullOrWhiteSpace(_datePickerTextBox?.Text) == false)
{
// If datepicker has any text, hide watermark.
VisualStateManager.GoToState(this, "Unwatermarked", useTransitions);
}
else
{
VisualStateManager.GoToState(this, "Watermarked", useTransitions);
}
}









share|improve this question



























    0















    I'm inheriting DatePicker in order to add IsReadOnly and InputRequired behaviour. have made corresponding visual states for both in CommonStates. The reason I have put it in CommonStates, is that they are both mutually exclusive with the Normal state (I checked TextBox and it the same implementation for IsReadOnly).



    The problem I encounter is that even if I apply the InputRequired state in OnApplyTemplate, it does not actually update the appearance. It does however after a MouseEnter event, leading my to conclude that it is only on the initial construction/rendering.



    From looking into the source code of Control I can see that in OnPostApplyTemplate, it calls an internal virtual UpdateVisualState method, which is overridden in DatePicker, and which applies the "Normal" state, meaning that my visual state update in OnApplyTemplate is overwritten by Control.



    As a workaround I can update the visual state in the Loaded event, and I can also make it a separate VisualStateGroup, and have some more complex logic in the code to determine wether the apply the visual state (e.g. inputrequired should not be applied with disabled/readonly), but both of these feel a little bit hacked.



    So my questions is: is there a correct way to inject new visual states in the CommonStates of a control in this way? Or is the best way to listen for the Loaded event and update visual state here (and are there any gotchas in this case)?



    Visual states:



        <VisualStateGroup x:Name="CommonStates">
    <VisualState x:Name="Normal"/>
    <VisualState x:Name="MouseOver">
    <Storyboard>
    <ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetName="Background" Storyboard.TargetProperty="Fill">
    <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource CommonHoverBackgroundColor}"/>
    </ObjectAnimationUsingKeyFrames>
    <ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetName="Background" Storyboard.TargetProperty="Stroke">
    <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource CommonHoverBorderColor}"/>
    </ObjectAnimationUsingKeyFrames>
    </Storyboard>
    </VisualState>
    <VisualState x:Name="ReadOnly">
    <Storyboard>
    <DoubleAnimation To="1" Duration="0" Storyboard.TargetName="ReadOnlyVisual" Storyboard.TargetProperty="Opacity"/>
    </Storyboard>
    </VisualState>
    <VisualState x:Name="InputRequired">
    <Storyboard>
    <ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetName="Background" Storyboard.TargetProperty="Fill">
    <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource CommonRequiredBackgroundColor}"/>
    </ObjectAnimationUsingKeyFrames>
    </Storyboard>
    </VisualState>
    <VisualState x:Name="Disabled">
    <Storyboard>
    <DoubleAnimation To="1" Duration="0" Storyboard.TargetName="PART_DisabledVisual" Storyboard.TargetProperty="Opacity"/>
    </Storyboard>
    </VisualState>
    </VisualStateGroup>


    UpdateVisualState in codebehind



    private void UpdateVisualState(bool useTransitions = true)
    {
    // Common states
    if (IsEnabled == false)
    {
    VisualStateManager.GoToState(this, "Disabled", useTransitions);
    }
    else if (IsMouseOver)
    {
    VisualStateManager.GoToState(this, "MouseOver", useTransitions);
    }
    else if (IsReadOnly)
    {
    VisualStateManager.GoToState(this, "ReadOnly", useTransitions);
    }
    if (CheckInputRequired())
    {
    VisualStateManager.GoToState(this, "Required", useTransitions);
    }
    else
    {
    VisualStateManager.GoToState(this, "Normal", useTransitions);
    }

    if (IsKeyboardFocusWithin)
    {
    VisualStateManager.GoToState(this, "Focused", useTransitions);
    }
    else
    {
    VisualStateManager.GoToState(this, "Unfocused", useTransitions);
    }

    // Clearable states
    if (IsReadOnly == false && AllowNull && SelectedDate != null)
    {
    VisualStateManager.GoToState(this, "Clearable", useTransitions);
    }
    else
    {
    VisualStateManager.GoToState(this, "Unclearable", useTransitions);
    }

    // Watermark states
    if (IsKeyboardFocusWithin)
    {
    // When control has keyboard focus, always hide watermark.
    VisualStateManager.GoToState(this, "Unwatermarked", useTransitions);
    }
    else if (string.IsNullOrWhiteSpace(_datePickerTextBox?.Text) == false)
    {
    // If datepicker has any text, hide watermark.
    VisualStateManager.GoToState(this, "Unwatermarked", useTransitions);
    }
    else
    {
    VisualStateManager.GoToState(this, "Watermarked", useTransitions);
    }
    }









    share|improve this question

























      0












      0








      0








      I'm inheriting DatePicker in order to add IsReadOnly and InputRequired behaviour. have made corresponding visual states for both in CommonStates. The reason I have put it in CommonStates, is that they are both mutually exclusive with the Normal state (I checked TextBox and it the same implementation for IsReadOnly).



      The problem I encounter is that even if I apply the InputRequired state in OnApplyTemplate, it does not actually update the appearance. It does however after a MouseEnter event, leading my to conclude that it is only on the initial construction/rendering.



      From looking into the source code of Control I can see that in OnPostApplyTemplate, it calls an internal virtual UpdateVisualState method, which is overridden in DatePicker, and which applies the "Normal" state, meaning that my visual state update in OnApplyTemplate is overwritten by Control.



      As a workaround I can update the visual state in the Loaded event, and I can also make it a separate VisualStateGroup, and have some more complex logic in the code to determine wether the apply the visual state (e.g. inputrequired should not be applied with disabled/readonly), but both of these feel a little bit hacked.



      So my questions is: is there a correct way to inject new visual states in the CommonStates of a control in this way? Or is the best way to listen for the Loaded event and update visual state here (and are there any gotchas in this case)?



      Visual states:



          <VisualStateGroup x:Name="CommonStates">
      <VisualState x:Name="Normal"/>
      <VisualState x:Name="MouseOver">
      <Storyboard>
      <ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetName="Background" Storyboard.TargetProperty="Fill">
      <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource CommonHoverBackgroundColor}"/>
      </ObjectAnimationUsingKeyFrames>
      <ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetName="Background" Storyboard.TargetProperty="Stroke">
      <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource CommonHoverBorderColor}"/>
      </ObjectAnimationUsingKeyFrames>
      </Storyboard>
      </VisualState>
      <VisualState x:Name="ReadOnly">
      <Storyboard>
      <DoubleAnimation To="1" Duration="0" Storyboard.TargetName="ReadOnlyVisual" Storyboard.TargetProperty="Opacity"/>
      </Storyboard>
      </VisualState>
      <VisualState x:Name="InputRequired">
      <Storyboard>
      <ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetName="Background" Storyboard.TargetProperty="Fill">
      <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource CommonRequiredBackgroundColor}"/>
      </ObjectAnimationUsingKeyFrames>
      </Storyboard>
      </VisualState>
      <VisualState x:Name="Disabled">
      <Storyboard>
      <DoubleAnimation To="1" Duration="0" Storyboard.TargetName="PART_DisabledVisual" Storyboard.TargetProperty="Opacity"/>
      </Storyboard>
      </VisualState>
      </VisualStateGroup>


      UpdateVisualState in codebehind



      private void UpdateVisualState(bool useTransitions = true)
      {
      // Common states
      if (IsEnabled == false)
      {
      VisualStateManager.GoToState(this, "Disabled", useTransitions);
      }
      else if (IsMouseOver)
      {
      VisualStateManager.GoToState(this, "MouseOver", useTransitions);
      }
      else if (IsReadOnly)
      {
      VisualStateManager.GoToState(this, "ReadOnly", useTransitions);
      }
      if (CheckInputRequired())
      {
      VisualStateManager.GoToState(this, "Required", useTransitions);
      }
      else
      {
      VisualStateManager.GoToState(this, "Normal", useTransitions);
      }

      if (IsKeyboardFocusWithin)
      {
      VisualStateManager.GoToState(this, "Focused", useTransitions);
      }
      else
      {
      VisualStateManager.GoToState(this, "Unfocused", useTransitions);
      }

      // Clearable states
      if (IsReadOnly == false && AllowNull && SelectedDate != null)
      {
      VisualStateManager.GoToState(this, "Clearable", useTransitions);
      }
      else
      {
      VisualStateManager.GoToState(this, "Unclearable", useTransitions);
      }

      // Watermark states
      if (IsKeyboardFocusWithin)
      {
      // When control has keyboard focus, always hide watermark.
      VisualStateManager.GoToState(this, "Unwatermarked", useTransitions);
      }
      else if (string.IsNullOrWhiteSpace(_datePickerTextBox?.Text) == false)
      {
      // If datepicker has any text, hide watermark.
      VisualStateManager.GoToState(this, "Unwatermarked", useTransitions);
      }
      else
      {
      VisualStateManager.GoToState(this, "Watermarked", useTransitions);
      }
      }









      share|improve this question














      I'm inheriting DatePicker in order to add IsReadOnly and InputRequired behaviour. have made corresponding visual states for both in CommonStates. The reason I have put it in CommonStates, is that they are both mutually exclusive with the Normal state (I checked TextBox and it the same implementation for IsReadOnly).



      The problem I encounter is that even if I apply the InputRequired state in OnApplyTemplate, it does not actually update the appearance. It does however after a MouseEnter event, leading my to conclude that it is only on the initial construction/rendering.



      From looking into the source code of Control I can see that in OnPostApplyTemplate, it calls an internal virtual UpdateVisualState method, which is overridden in DatePicker, and which applies the "Normal" state, meaning that my visual state update in OnApplyTemplate is overwritten by Control.



      As a workaround I can update the visual state in the Loaded event, and I can also make it a separate VisualStateGroup, and have some more complex logic in the code to determine wether the apply the visual state (e.g. inputrequired should not be applied with disabled/readonly), but both of these feel a little bit hacked.



      So my questions is: is there a correct way to inject new visual states in the CommonStates of a control in this way? Or is the best way to listen for the Loaded event and update visual state here (and are there any gotchas in this case)?



      Visual states:



          <VisualStateGroup x:Name="CommonStates">
      <VisualState x:Name="Normal"/>
      <VisualState x:Name="MouseOver">
      <Storyboard>
      <ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetName="Background" Storyboard.TargetProperty="Fill">
      <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource CommonHoverBackgroundColor}"/>
      </ObjectAnimationUsingKeyFrames>
      <ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetName="Background" Storyboard.TargetProperty="Stroke">
      <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource CommonHoverBorderColor}"/>
      </ObjectAnimationUsingKeyFrames>
      </Storyboard>
      </VisualState>
      <VisualState x:Name="ReadOnly">
      <Storyboard>
      <DoubleAnimation To="1" Duration="0" Storyboard.TargetName="ReadOnlyVisual" Storyboard.TargetProperty="Opacity"/>
      </Storyboard>
      </VisualState>
      <VisualState x:Name="InputRequired">
      <Storyboard>
      <ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetName="Background" Storyboard.TargetProperty="Fill">
      <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource CommonRequiredBackgroundColor}"/>
      </ObjectAnimationUsingKeyFrames>
      </Storyboard>
      </VisualState>
      <VisualState x:Name="Disabled">
      <Storyboard>
      <DoubleAnimation To="1" Duration="0" Storyboard.TargetName="PART_DisabledVisual" Storyboard.TargetProperty="Opacity"/>
      </Storyboard>
      </VisualState>
      </VisualStateGroup>


      UpdateVisualState in codebehind



      private void UpdateVisualState(bool useTransitions = true)
      {
      // Common states
      if (IsEnabled == false)
      {
      VisualStateManager.GoToState(this, "Disabled", useTransitions);
      }
      else if (IsMouseOver)
      {
      VisualStateManager.GoToState(this, "MouseOver", useTransitions);
      }
      else if (IsReadOnly)
      {
      VisualStateManager.GoToState(this, "ReadOnly", useTransitions);
      }
      if (CheckInputRequired())
      {
      VisualStateManager.GoToState(this, "Required", useTransitions);
      }
      else
      {
      VisualStateManager.GoToState(this, "Normal", useTransitions);
      }

      if (IsKeyboardFocusWithin)
      {
      VisualStateManager.GoToState(this, "Focused", useTransitions);
      }
      else
      {
      VisualStateManager.GoToState(this, "Unfocused", useTransitions);
      }

      // Clearable states
      if (IsReadOnly == false && AllowNull && SelectedDate != null)
      {
      VisualStateManager.GoToState(this, "Clearable", useTransitions);
      }
      else
      {
      VisualStateManager.GoToState(this, "Unclearable", useTransitions);
      }

      // Watermark states
      if (IsKeyboardFocusWithin)
      {
      // When control has keyboard focus, always hide watermark.
      VisualStateManager.GoToState(this, "Unwatermarked", useTransitions);
      }
      else if (string.IsNullOrWhiteSpace(_datePickerTextBox?.Text) == false)
      {
      // If datepicker has any text, hide watermark.
      VisualStateManager.GoToState(this, "Unwatermarked", useTransitions);
      }
      else
      {
      VisualStateManager.GoToState(this, "Watermarked", useTransitions);
      }
      }






      wpf-controls visualstatemanager visualstates visualstategroup






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked Nov 14 '18 at 12:51









      sondergardsondergard

      2,3211020




      2,3211020
























          0






          active

          oldest

          votes











          Your Answer






          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "1"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          autoActivateHeartbeat: false,
          convertImagesToLinks: true,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: 10,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














          draft saved

          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53300684%2fadding-new-visual-state-to-commonstates-in-inherited-controls%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          0






          active

          oldest

          votes








          0






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes
















          draft saved

          draft discarded




















































          Thanks for contributing an answer to Stack Overflow!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53300684%2fadding-new-visual-state-to-commonstates-in-inherited-controls%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          Popular posts from this blog

          Xamarin.iOS Cant Deploy on Iphone

          Glorious Revolution

          Dulmage-Mendelsohn matrix decomposition in Python