您的位置:首页 > 其它

跟着小王学wpf系列之 常用控件布局控件

2011-04-04 00:31 666 查看
控件之Stack

在wpf中,继承与ContentContorl的控件都具有Content属性,可以设置为另一个控件或者字符串等。一般来说这个对象不是字符串就是继承与Uielement的类的实例。问题在于content只能设置一个对象,对于一个窗口来说这远远不够,因此wpf针对这个问题,设计了几个控件,这些控件的父类都是panel(面板),而将控件和其他element对象放置在面板上的方法,称之为layout(布局)。

比较重要的几个panel类及其继承关系。

对于传统的windows窗口来说上面的控件都有精确地尺寸和位置,但是在wpf中,layout(动态布局)是很常用的。所谓动态布局,是指根据不同的布局规则将每个要不拘的元素的大小和位置确定,每一个继承与panel的控件都有自己一个独特的布局规则(layout model)。

Panel定义了一个children属性,用来存储子对象(child element)。Children的类型是UielementCollection,也就是Uielement对象的集合(Collection)。因此面板的子对象可以是Image、shape、TextBlock、和contorl对象,当然面板的子对象还可以是另一个面板对象。

下面讨论StackPanel:

class MyWindow:Window

{

[STAThread]

static void Main(string[] args)

{

Application app = new Application();

app.Run(new MyWindow());

}

public MyWindow()

{

Title = "Stack Ten Buttons";

StackPanel stack = new StackPanel();

Content = stack;

Random rand = new Random();

for (int i = 0; i < 10;i++ )

{

Button btn = new Button();

btn.Name = ((Char)('A' + i)).ToString();

btn.FontSize += rand.Next(10);

btn.Content = "Button " + btn.Name;

btn.Click += new RoutedEventHandler(btn_Click);

stack.Children.Add(btn);

}

}

void btn_Click(object sender, RoutedEventArgs e)

{

Button btn = sender as Button;

if (btn!=null)

{

MessageBox.Show("Button " + btn.Name + " be Clicked!", "Button Clicked");

}

}

}

可以看出这么多的button是垂直方式排列 每个子对象占满整个水平区域。

给stack添加背景画刷

stack.Background = new LinearGradientBrush(Colors.Blue, Colors.Red, new Point(0, 0), new Point(1, 1));

让所有子元素水平方式排列

stack.Orientation = Orientation.Horizontal;

当添加下面语句后,每个button将会自动调整大小适应字体,并且居中显示。

btn.VerticalAlignment = VerticalAlignment.Center;

另一种方式是调整stack的大小,而不是改变按钮的大小。

stack.VerticalAlignment = VerticalAlignment.Center;

最好的方式是让窗口去适应控件的大小

SizeToContent = SizeToContent.WidthAndHeight;

ResizeMode = ResizeMode.CanResize;

如果不喜欢所有的按钮都黏在一起,想让他们之间有空隙,则:btn.Margin = new Thickness(5); stack.Margin = new Thickness(5);

我们选择显示3列每列十个按钮:

class MyWindow:Window

{

[STAThread]

static void Main(string[] args)

{

Application app = new Application();

app.Run(new MyWindow());

}

public MyWindow()

{

Title = "Stack Ten Buttons";

AddHandler(Button.ClickEvent, new RoutedEventHandler(btn_Click));

StackPanel stack = new StackPanel();

Content = stack;

stack.Margin = new Thickness(5);

stack.Orientation = Orientation.Horizontal;

Random rand = new Random();

for (int j = 0; j < 3;j++ )

{

StackPanel tstack = new StackPanel();

tstack.Margin = new Thickness(5);

//tstack.Name = j.ToString() + "_Stack";

stack.Children.Add(tstack);

for (int i = 0; i < 10; i++)

{

Button btn = new Button();

btn.FontSize += rand.Next(100) / 10;

btn.Content = "Button " + (10*j+i);

btn.Margin = new Thickness(5);

tstack.Children.Add(btn);

}

}

SizeToContent = SizeToContent.WidthAndHeight;

ResizeMode = ResizeMode.CanResize;

}

void btn_Click(object sender, RoutedEventArgs e)

{

Button btn = e.Source as Button;

if (btn!=null)

{

MessageBox.Show(btn.Content + " be Clicked!", "Button Clicked");

}

}

当然也可以选择3行,10列:

关于这里的事件处理器我们要讨论一下,以前在。Net中所有控件的事件是由控件发出,然后激活处理器,在wpf中,我们可以用窗口去监控所有的事件,在窗口中处理所有控件的事件。

AddHandler(Button.ClickEvent, new RoutedEventHandler(btn_Click));

上面这句话是处理一个按钮的点击事件,处理器为btn_Click。

我给按钮也添加上事件处理器:btn.Click+=new RoutedEventHandler(btn_Click1);

执行程序后会发现,按钮自己的事件处理器会首先被调用,然后才会调用窗口的处理器,说明在事件发生的时候,按钮发出一个ClickEvent,在这个事件的处理器委托链表上有两个(我们看到的两个,其他隐藏的不讨论),一个是我们定义的按钮事件处理器,一个是通知窗口有一个按钮点击事件(ClickEvent)发生。

当然 如果控件过多显示不下的时候我们需要滚动条,在wpf中我们不想去添加滚动条,而是使用一个ScrollViewer控件,将它的content设置为原来要设置给窗口content的对象,这样当控件显示不下的时候就会自动出现滚动条。

public MyWindow()

{

Title = "Stack Ten Buttons";

AddHandler(Button.ClickEvent, new RoutedEventHandler(btn_Click));

ScrollViewer sv = new ScrollViewer();

StackPanel stack = new StackPanel();

sv.Content = stack;

Content = sv;

stack.Margin = new Thickness(5);

this.HorizontalAlignment = HorizontalAlignment.Center;

stack.HorizontalAlignment = HorizontalAlignment.Center;

Random rand = new Random();

for (int i = 0; i < 50; i++)

{

Button btn = new Button();

btn.FontSize += rand.Next(100) / 10;

btn.Content = "Button " + i;

btn.Margin = new Thickness(5);

stack.Children.Add(btn);

}

}

可以看到显示不下的时候,窗口上自动出现了竖直滚动条。

但是如果我们将stack设置为stack.Orientation = Orientation.Horizontal;水平显示的时候,并没有水平滚动条出现。其实跟StackPanel一样,ScrollViewer默认情况下是只显示竖直滚动条。当我们选择:

sv.HorizontalScrollBarVisibility = ScrollBarVisibility.Auto;后就两个滚动条都会显示了。

当我们点击滚动条下面的箭头按钮的时候,我们发现,void btn_Click(object sender, RoutedEventArgs e)被调用,我们把事件处理器改成如下样式:

void btn_Click(object sender, RoutedEventArgs e)

{

Button btn = e.Source as Button;

MessageBox.Show(btn.Content + " be Clicked too!", "Button Clicked");

}

运行程序后会发现,出现了一个异常:

说明此时btn为null.为什么会发生以上情况呢:

我们但不调试发现,引发这个事件的e.Source是一个ScrollVie对象,ScrollViewer是不能转化成一个button,所以转化完成后btn为空。但是ScrollViewer是没有click事件的,为什么会触发这个事件呢?我们发现这个事件的RoutedEventArgs e的OriginalSource属性如下:

OriginalSource = {System.Windows.Controls.Primitives.RepeatButton}

说明其实是这个触发了button.clickevent.

所以在将一个事件处理器追加到父控件来处理的时候就需要考虑到这些额外的处理,对于以上问题的一个解决方法是将事件绑定到StackPanel而不是绑定到窗口。

stack.AddHandler(Button.ClickEvent, new RoutedEventHandler(btn_Click));

在有限的空间中,放入更多的Element,滚动条时传统的解决方案,在wpf中的解决方法是ViewBox.

Viewbox vb = new Viewbox();

StackPanel stack = new StackPanel();

Content = vb;

vb.Child = stack;

这样整个stack就会被全部显示在当前的窗口内了,当然对于有文字标签的控件来说显示还是个问题:

我们看看在一个按钮中放置一组控件的方式:

public MyWindow()

{

Title = "Stack Buttons";

Button btn = new Button();

btn.HorizontalAlignment = HorizontalAlignment.Center;

btn.VerticalAlignment = VerticalAlignment.Center;

Content = btn;

StackPanel stack = new StackPanel();

Viewbox vb = new Viewbox();

vb.Child = stack;

btn.Content = vb;

stack.Children.Add(Zigzog(10));

Uri uri = new Uri("pack://application:,,,/1.png");

BitmapImage bi = new BitmapImage(uri);

Image img = new Image();

img.Margin = new Thickness(0, 10, 0, 0);

img.Source = bi;

stack.Children.Add(img);

Label lbl = new Label();

lbl.Content = "_Test Label";

stack.Children.Add(lbl);

stack.Children.Add(Zigzog(0));

}

这是实例。下面我们用groupbox来装载一组radiobutton,但是其实groupbox中装的是一个stackpanel。

public MyWindow()

{

Title = "Group box";

SizeToContent = SizeToContent.WidthAndHeight;

GroupBox groupbox = new GroupBox();

groupbox.Header = "Window Style";

groupbox.Margin = new Thickness(96);

groupbox.Padding = new Thickness(5);

Content = groupbox;

StackPanel stack = new StackPanel();

groupbox.Content = stack;

stack.Children.Add(CreateRadiobutton("No Border", WindowStyle.None));

stack.Children.Add(CreateRadiobutton("Single border", WindowStyle.SingleBorderWindow));

stack.Children.Add(CreateRadiobutton("3D Border", WindowStyle.ThreeDBorderWindow));

stack.Children.Add(CreateRadiobutton("Tool window", WindowStyle.ToolWindow));

AddHandler(RadioButton.CheckedEvent, new RoutedEventHandler(ButtonChecked));

}

private RadioButton CreateRadiobutton(string btntext,WindowStyle Windowstyle)

{

RadioButton radio = new RadioButton();

radio.Content = btntext;

radio.Tag = Windowstyle;

radio.Margin = new Thickness(5);

radio.IsChecked = (WindowStyle == Windowstyle);

return radio;

}

private void ButtonChecked(object sender,RoutedEventArgs args)

{

RadioButton radio = args.Source as RadioButton;

WindowStyle = (WindowStyle)radio.Tag;

}

下面我们讲解一下Wrappanel,Wrappanel提供一个功能,当他的内容在当前行无法显示完全的情况下会自动换行。

public MyWindow()

{

Title = "WrapPanel box";

ScrollViewer Scrollviewer = new ScrollViewer();

WrapPanel wrap = new WrapPanel();

Content = Scrollviewer;

Scrollviewer.Content = wrap;

for (int i = 1; i <= 20;i++ )

{

Button btn = new Button();

btn.Name = "Button" + i.ToString("00");

btn.Content = btn.Name;

wrap.Children.Add(btn);

}

}

下面,我们试着来做一个检索文件的系统,

class FileSystemInfoButton:Button

{

FileSystemInfo info;

public FileSystemInfoButton()

:this(

new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)))

{

}

public FileSystemInfoButton(FileSystemInfo fs)

{

this.info = fs;

Content = info.Name;

if (info is DirectoryInfo)

{

FontWeight = FontWeights.Bold;

}

Margin = new Thickness(10);

}

public FileSystemInfoButton(FileSystemInfo fs, string str):this(fs)

{

Content = str;

}

protected override void OnClick()

{

//base.OnClick();

if (info is FileInfo)

{

//如果当前点击的按钮是一个文件,那么打开这个文件

Process.Start(info.FullName);

}

else if (info is DirectoryInfo)

{

DirectoryInfo dir = info as DirectoryInfo;

Application.Current.MainWindow.Title = dir.FullName;

Panel panel = this.Parent as Panel;

panel.Children.Clear();

if (dir.Parent!=null)

{

//如果打开的是一个目录,那么首先设置一个。。。按钮,作为父目录的指向。

panel.Children.Add(new FileSystemInfoButton(dir.Parent, "..."));

}

foreach (FileSystemInfo fsinfo in dir.GetFileSystemInfos())

{

//然后遍历整个目录,将所有的子目录或者文件显示出来

panel.Children.Add(new FileSystemInfoButton(fsinfo));

}

base.OnClick();

}

}

}

在主窗口中如下操作:

public MyWindow()

{

Title = "WrapPanel box";

ScrollViewer Scrollviewer = new ScrollViewer();

WrapPanel wrap = new WrapPanel();

Content = Scrollviewer;

Scrollviewer.Content = wrap;

wrap.Children.Add(new FileSystemInfoButton());

}

下面来讨论Dock。

DockPanel dock = new DockPanel();

Content = dock;

for (int i = 0; i < 17;i++ )

{

Button btn = new Button();

btn.Content = "Button " + i;

dock.Children.Add(btn);

btn.SetValue(DockPanel.DockProperty, (Dock)(i % 4));

}

可以看到这些按钮的排列以及分布的方式。

最后一个按钮16填充了剩余的所有区域,是因为在DockPanel中默认最后一个元素填充。也可以设置为不填充:dock.LastChildFill = false;

下面做一个例子,在一个Dockpanel中填充一个Menu,一个Toolbar,一个StatusBar一个ListBox一个textbox. Textbox是最后一个控件,所以它填充最后的区域。

public MyWindow()

{

Title = "WrapPanel box";

DockPanel dock = new DockPanel();

Content = dock;

Menu menu = new Menu();

MenuItem menuitem = new MenuItem();

menuitem.Header = "Item";

menu.Items.Add(menuitem);

DockPanel.SetDock(menu, Dock.Top);

dock.Children.Add(menu);

ToolBar toolbar = new ToolBar();

toolbar.Header = "Toolbar";

DockPanel.SetDock(toolbar, Dock.Top);

dock.Children.Add(toolbar);

StatusBar status = new StatusBar();

StatusBarItem statusitem = new StatusBarItem();

statusitem.Content = "Status";

status.Items.Add(statusitem);

DockPanel.SetDock(status, Dock.Bottom);

dock.Children.Add(status);

ListBox list = new ListBox();

list.Items.Add("list box item");

DockPanel.SetDock(list, Dock.Left);

dock.Children.Add(list);

TextBox txt = new TextBox();

txt.AcceptsReturn = true;

dock.Children.Add(txt);

txt.Focus();

}

结果如图所示。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: