Создать эскиз неактивного окна C # WPF - PullRequest
7 голосов
/ 29 июня 2010

Я просмотрел много тем здесь и нашел информацию, но не нашел ничего, касающегося моего вопроса.

Я хочу сделать так, чтобы при запуске приложения пользователем открывалось главное окно (не MDI) с четырьмя графическими полями, в каждом из которых отображалось изображение формы, которая открывается при нажатии на нее. После того, как выбранная форма открыта и внесены изменения, если щелкнуть мышью, чтобы свернуть / закрыть форму, она (казалось бы) свернется в графическое окно, показывая изображение в реальном времени того, как форма выглядит в виде эскиза.

У меня вопрос, как мне превратить форму в изображение, чтобы я мог использовать изображение в качестве эскиза в графическом окне?

Кроме того ... Может ли кто-нибудь указать мне на некоторые ресурсы, которые помогут мне понять, как анимировать «минимизацию» в окне изображения?

Я не прошу никого делать свою работу за меня, потому что я хотел бы научиться этому сам, но я немного застрял.

Наконец, я не уверен, что в этом замешано, поэтому я не знаю, какие теги ставить для этого поста. Я добавлю теги, когда выясню, чтобы другие могли найти эту информацию.

РЕДАКТИРОВАТЬ: Извините, это в WPF. Не был уверен, что все будет иначе. Я до сих пор не особо разбираюсь в WPF.

Ответы [ 3 ]

6 голосов
/ 29 июня 2010

Вы можете использовать VisualBrush, здесь приведен быстрый пример кнопки с фоном, установленным на уменьшенную версию стековой панели.

 <DockPanel>
        <StackPanel x:Name="myRect" >
            <TextBox Text="MyTexasdfasdfasdfasdfasdft" Height="50" />
            <CheckBox IsChecked="True"  />
            <Rectangle Fill="Red" Width="100" Height="100" />
        </StackPanel>


        <Button>
            <Button.Background>
                <VisualBrush TileMode="None"  Viewport="0,0,1,1" Visual="{Binding ElementName=myRect}" >
                    <VisualBrush.Transform>
                        <ScaleTransform ScaleX="0.3" ScaleY="0.3" />
                    </VisualBrush.Transform>
                </VisualBrush>
            </Button.Background>
        </Button>
    </DockPanel>

Редактировать: хотя это решение работает для копирования содержимогоэкран, когда материал на экране скрыт или удален, как и VisualBrush.Чтобы сохранить изображение, необходимо сделать элемент управления растровым.Это можно сделать с помощью RenderTargetBitMap

// CenterControl is the target to render, ShowControl is the control to render the CenterControl onto.
var rtb = new RenderTargetBitmap((int)CenterControl.ActualWidth, (int)CenterControl.ActualHeight, 96, 96,
                                             PixelFormats.Pbgra32);
            rtb.Render(CenterControl);
            var bgBrush = new ImageBrush(rtb) {Transform = new ScaleTransform(0.1, 0.1)};
            ShowControl.Background = bgBrush;
3 голосов
/ 03 июля 2010

Я собираюсь предположить, что вам нужны отдельные отдельные окна, которые можно перетаскивать независимо друг от друга по экрану среди окон других приложений. (Если это предположение неверно и вам больше подходит интерфейс, подобный MDI, взгляните на ответ Роба.)

Я бы реализовал подкласс Expander, который принимает окно и:

  1. Когда IsExpanded = false, он представляет сам контент окна, используя ContentPresenter, но
  2. Когда IsExpanded = true, оно позволяет окну представлять свой собственный контент, но использует VisualBrush с прямоугольником для отображения этого контента

Он может иметь имя «WindowExpander», и его свойство Content будет установлено на фактический объект Window, который будет отображаться при развертывании Expander. Например, его можно использовать одним из следующих способов, в зависимости от того, как определяется ваша Windows:

<UniformGrid Rows="2" Columns="2">
  <local:WindowExpander Window="{StaticResource Form1Window}" />
  <local:WindowExpander Window="{StaticResource Form2Window}" />
  <local:WindowExpander Window="{StaticResource Form3Window}" />
  <local:WindowExpander Window="{StaticResource Form4Window}" />
</UniformGrid>

<UniformGrid Rows="2" Columns="2">
  <local:WindowExpander><Window Width="800" Height="600"><local:Form1 /></Window></local:WindowExpander>
  <local:WindowExpander><Window Width="800" Height="600"><local:Form2 /></Window></local:WindowExpander>
  <local:WindowExpander><Window Width="800" Height="600"><local:Form3 /></Window></local:WindowExpander>
  <local:WindowExpander><Window Width="800" Height="600"><local:Form4 /></Window></local:WindowExpander>
</UniformGrid>

<ItemsControl ItemsSource="{Binding Forms}">
  <ItemsControl.ItemsPanel>
    <ItemsPanelTemplate><UniformGrid Rows="2" Columns="2"/></ItemsPanelTemplate>
  </ItemsControl.ItemsPanel>
</ItemsControl>

Реализация WindowExpander была бы ToggleButton, содержащей ViewBox, который отображал миниатюру, например так:

<Style TargetType="local:WindowExpander">
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="local:WindowExpander">
        <ToggleButton IsChecked="{TemplateBinding IsExpanded}">
          <Viewbox IsHitTestVisible="False">
            <ContentPresenter Content="{Binding Header} />
          </Viewbox>
        </ToggleButton>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>

Я думаю, вы, вероятно, захотите реализовать WindowExpander примерно так:

[ContentProperty("Window")]
public class WindowExpander : Expander
{
  Storyboard _storyboard;

  public static WindowExpander()
  {
    DefaultStyleKeyProperty.OverrideMetadata(typeof(WindowExpander), new FrameworkPropertyMetadata(typeof(WindowExpander)));
    IsExpandedProperty.OverrideMetadata(typeof(WindowExpander), new FrameworkPropertyMetadata
    {
      PropertyChangedCallback = (obj, e) =>
        {
          var expander = (WindowExpander)obj;
          if(expander.Window!=null)
          {
            expander.SwapContent(expander.Window);
            expander.AnimateWindow();
          }
        }
    });
  }

  public Window Window { get { return (Window)GetValue(WindowProperty); } set { SetValue(WindowProperty, value); } }
  public static readonly DependencyProperty WindowProperty = DependencyProperty.Register("Window", typeof(Window), typeof(WindowExpander), new UIPropertyMetadata
  {
    PropertyChangedCallback = (obj, e) =>
    {
      var expander = (WindowExpander)obj;
      var oldWindow = (Window)e.OldValue;
      var newWindow = (Window)e.NewValue;
      if(oldWindow!=null)
      {
        if(!expander.IsExpanded) expander.SwapContent(oldWindow);
        oldWindow.StateChanged -= expander.OnStateChanged;
      }
      expander.ConstructLiveThumbnail();
      if(newWindow!=null)
      {
        if(!expander.IsExpanded) expander.SwapContent(newWindow);
        newWindow.StateChanged -= expander.OnStateChanged;
      }
    }
  });

  private void ConstructLiveThumbnail()
  {
    if(Window==null)
      Header = null;
    else
    {
      var rectangle = new Rectangle { Fill = new VisualBrush { Visual = (Visual)Window.Content } };
      rectangle.SetBinding(Rectangle.WidthProperty, new Binding("Width") { Source = Window });
      rectangle.SetBinding(Rectangle.HeightProperty, new Binding("Height") { Source = Window });
      Header = rectangle;
    }
  }

  private void SwapContent(Window window)
  {
    var a = Header; var b = window.Content;
    Header = null;  window.Content = null;
    Header = b;     window.Content = a;
  }

  private void AnimateWindow()
  {
    if(_storyboard!=null)
      _storyboard.Stop(Window);

    var myUpperLeft = PointToScreen(new Point(0, 0));
    var myLowerRight = PointToScreen(new Point(ActualWidth, ActualHeight));
    var myRect = new Rect(myUpperLeft, myLowerRight);
    var winRect = new Rect(Window.Left, Window.Top, Window.Width, Window.Height);

    var fromRect = IsExpanded ? myRect : winRect;  // Rect where the window will animate from
    var toRect = IsExpanded ? winRect : myRect;    // Rect where the window will animate to

    _storyboard = new Storyboard { FillBehavior = FillBehavior.Stop };
    // ... code to build storyboard here ...
    // ... should animate "Top", "Left", "Width" and "Height" of window from 'fromRect' to 'toRect' using desired timeframe
    // ... should also animate Visibility=Visibility.Visible at time=0

    _storyboard.Begin(Window);
    Window.Visibility = IsExpanded ? Visibility.Visible : Visibility.Hidden;
  }

  private void OnStateChanged(object sender, EventArgs e)
  {
    if(IsExpanded && Window.WindowState == WindowState.Minimized)
    {
      Window.WindowState = WindowState.Normal;
      IsExpanded = false;
    }
  }
}

Приведенный выше код пропускает шаги для создания анимации. Это также не было проверено - это было просто написано быстро от макушки моей головы. Я надеюсь, что это работает для вас.

Как это работает: IsExpanded контролирует видимость окна, за исключением того, что при изменении IsExpanded раскадровка временно заставляет окно оставаться видимым достаточно долго для запуска анимации. В любой момент либо Window, либо ContentPresenter в шаблоне содержат содержимое окна. Вы можете сказать, что всякий раз, когда расширитель не раскрывается (нет окна), Контент «украден» из окна для использования в WindowExpander. Это делается методом SwapContent. Он помещает прямоугольник, нарисованный с помощью VisualBrush, в окно, а фактическое содержимое окна - в заголовок, который является миниатюрой, отображаемой на кнопке ToggleButton.

Этот метод основан на том факте, что VisualBrush не работает с невидимым Visual, потому что визуал «Content» фактически всегда виден - он всегда является потомком Window или ContentPresenter внутри ViewBox.

Поскольку используется VisualBrush, миниатюра всегда дает предварительный просмотр в реальном времени.

Одно предупреждение: не устанавливайте DataContext и не создавайте ресурсы на уровне окна. Если вы это сделаете, когда ваш контент «украден», у него не будет нужного DataContext или ресурсов, поэтому он не будет выглядеть правильно. Я бы порекомендовал использовать UserControl вместо Window для каждой формы, а вашей основной формой обернуть его в Window, как показано здесь:

<UniformGrid Rows="2" Columns="2">
  <local:WindowExpander><Window Width="800" Height="600"><local:Form1 /></Window></local:WindowExpander>
  <local:WindowExpander><Window Width="800" Height="600"><local:Form2 /></Window></local:WindowExpander>
  <local:WindowExpander><Window Width="800" Height="600"><local:Form3 /></Window></local:WindowExpander>
  <local:WindowExpander><Window Width="800" Height="600"><local:Form4 /></Window></local:WindowExpander>
</UniformGrid>
1 голос
/ 29 июня 2010

Если вы начинаете с WPF, то для того, что вы планируете делать, скорее всего, потребуется либо изучить Blend, чтобы определить условия и анимацию, либо углубиться в систему анимации, чтобы понять ее и от руки -код XAML.

На высоком уровне, я думаю, вы могли бы подойти к этому, определив каждую из ваших четырех «форм» как UserControls или ContentPresenters, возможно, с границей вокруг них.

Затем, когда «форма» находится в неактивном состоянии, используйте свойство LayoutTransform или RenderTransform вместе с другими свойствами позиционирования для позиционирования и сжатия. Как только ваш мозг привыкнет к Blend, на самом деле довольно легко определить это, используя «Состояния» и «Триггеры».

Чтобы добавить поведение для увеличения свернутой формы, обработайте событие «PreviewMouseDown» и в обработчике проверьте состояние формы.

Мне показались полезными для этого видео "Учись смешивать за 5 дней", но я признаюсь, что поделился своей путаницей; я не нашел единого места, где бы систематически преподавали XAML и WPF, не просто записавшись в сторонний учебный класс или пригласив наставника-консультанта. Не помогает, что в это время пятый день тренинга - «Скоро» или что все это связано с Silverlight, а не с WPF.

Но это начало; Видео "Learn Blend" находится здесь:

http://www.microsoft.com/expression/resources/blendtraining/

Вы также увидите ссылку на файл с именем «.toolbox», который я еще не пробовал.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...