创建非活动C#WPF窗口的缩略图

时间:2010-06-29 14:44:47

标签: c# wpf

我在这里查看了很多主题,并搜索了相关信息,但我没有找到与我的问题有关的任何内容。

我想要做的就是让用户启动应用程序时,主窗口(不是MDI)打开时有四个图像框,每个图像框显示一个图像,当它们点击它时会打开。打开选定的表单并进行更改后,如果他们单击以最小化/关闭表单,它将(看似)最小化到图像框中,显示表单在缩略图视图中的实时图像。

我的问题是,如何将图像制作成图像,以便将图像用作图像框中的缩略图?

此外......有人能指出一些资源的方向,这将有助于我弄清楚如何动画“最小化”到图像框中吗?

我不是要求任何人为我做我的工作,因为我想自己学习,但我有点卡住了。

最后,我不确定这涉及到什么,所以我不知道要为这篇文章添加什么标签。我会在我弄清楚时添加标签,以便其他人可以找到这些信息。

编辑:抱歉,它在WPF中。不确定它会有什么不同。我在WPF方面仍然没有特别的经验。

3 个答案:

答案 0 :(得分:6)

您可以使用VisualBrush,这是一个按钮的快速示例,其背景设置为stackpanel的缩小版本。

 <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;

答案 1 :(得分:3)

我将假设您需要实际单独的窗口,可以在其他应用程序的窗口中独立地在屏幕上拖放。 (如果这个假设不正确并且类似MDI的界面对你更好,请看看Rob的回答。)

我将实现一个接受Window的Expander子类:

  1. 当IsExpanded = false时,它使用ContentPresenter呈现窗口内容,但
  2. 当IsExpanded = true时,它会让窗口显示自己的内容,但使用带有矩形的VisualBrush来显示该内容
  3. 它可能被命名为“WindowExpander”,并且将其Content属性设置为展开Expander时要显示的实际Window对象。例如,它可以以下列方式之一使用,具体取决于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控制Window的可见性,但IsExpanded更改故事板时会暂时强制窗口保持足够长的时间以便动画运行。在任何给定时刻,模板中的Window或ContentPresenter都包含窗口的内容。您可能会说,无论何时扩展器未展开(无窗口),内容都会从窗口“被盗”,以便在WindowExpander中使用。这是通过SwapContent方法完成的。它将使用VisualBrush绘制的Rectangle放入Window,将Window的实际内容放入Header,这是ToggleButton上显示的缩略图。

    这种技术解决了VisualBrush无法在不可见的Visual上工作的事实,因为“Content”视觉实际上始终是可见的 - 它始终是ViewBox中的Window或ContentPresenter的子项。

    由于使用了VisualBrush,因此缩略图始终提供实时预览。

    一个警告:不要在Window级别设置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>
    

答案 2 :(得分:1)

如果您刚开始使用WPF,那么您计划要做的事情可能需要您学习Blend以定义条件和动画,或者深入了解动画系统以便理解它和手 - 编码XAML。

在高层次上,我想你可以通过将你的四个“形式”中的每一个定义为UserControls或ContentPresenters来实现这一点,也许是围绕它们的边框。

然后,当“表单”处于非活动状态时,请使用LayoutTransformRenderTransform属性以及其他定位属性来定位和缩小它。一旦你的大脑习惯于混合,实际上很容易使用“状态”和“触发器”来定义它。

要添加行为以增长最小化的表单,请处理“PreviewMouseDown”事件,并在处理程序中测试表单的状态。

我发现“5天学习混合”视频对此有用,但我承认分享你的困惑;我发现没有统一的地方以系统的方式教授XAML和WPF,而不是简单地参加第三方培训课程或打电话给导师顾问。在这个时候,培训的第五天是“即将推出”,或者整个事情都是关键的Silverlight而不是WPF,这没有任何帮助。

但是,这是一个开始; “学习混合”视频可在此处找到:

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

你还会看到一个名为“.toolbox”的链接,我还没有尝试过。