Я пытаюсь создать простой текстовый редактор на F# с помощью WinForms. Я хотел бы знать, как проще всего заставить элементы заполнять свои родительские контейнеры, и я обнаружил, что текстовое поле не заполняет своего родителя, несмотря на то, что для его свойства Dock
установлено значение DockStyle.Fill
. Я бы хотел, чтобы верхние MenuStrip
и RichTextBox
заполнили родительские контейнеры и заняли всю форму (как вы ожидаете увидеть в текстовом редакторе). Вот скриншот, демонстрирующий проблему:
Как видите, было бы неплохо иметь и MenuStrip, и RichTextBox для заполнения всей формы.
Вот код (вместе с файлом .fsproj
):
Программа.фс:
module FsEdit.Program
open System
open System.Windows.Forms
[<EntryPoint; STAThread>]
let main argv =
Application.Run FsEdit.MainForm.MainForm
0
MainForm.fs:
[<RequireQualifiedAccess>]
module FsEdit.MainForm
open System.Windows.Forms
// MenuBar
let private FileMenuTab =
new ToolStripMenuItem(
Text = "File"
)
let private EditMenuTab =
new ToolStripMenuItem(
Text = "Edit"
)
let private AboutMenuTab =
new ToolStripMenuItem(
Text = "About"
)
let private MainMenuStrip =
new MenuStrip(
Text = "MainMenuStrip"
)
let private allMenuStripItems =
[|
FileMenuTab
EditMenuTab
AboutMenuTab
|]
|> Array.map (fun tab -> tab :> ToolStripItem)
MainMenuStrip.Items.AddRange(allMenuStripItems)
// Text Editor
let private MainTextBox =
let richTextBox = new RichTextBox(
Text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut a eleifend nunc. Suspendisse non purus varius, ullamcorper arcu et, vehicula lacus. Integer pellentesque facilisis interdum. Aliquam id leo arcu. Nam mauris nisl, semper eget massa sed, aliquam convallis lacus. Etiam a neque blandit, sollicitudin nisl quis, ornare dui. Aliquam nec lorem sit amet arcu iaculis elementum rutrum eu velit. Curabitur dignissim blandit ligula at efficitur. Curabitur id justo quis tortor egestas ultrices. Nam arcu quam, ullamcorper id velit quis, aliquam finibus libero. Pellentesque semper fermentum sem a scelerisque. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae;"
)
richTextBox.Dock <- DockStyle.Fill
richTextBox.Anchor <- AnchorStyles.Left ||| AnchorStyles.Right ||| AnchorStyles.Top
richTextBox.AllowDrop <- true
richTextBox.AutoSize <- true
richTextBox
let private MainTextBoxPanel =
let p = new FlowLayoutPanel()
p.Dock <- DockStyle.Fill
p.WrapContents <- false
p.FlowDirection <- FlowDirection.TopDown
p.Anchor <- AnchorStyles.Left ||| AnchorStyles.Right ||| AnchorStyles.Top
p
MainTextBoxPanel.Controls.Add(MainMenuStrip)
MainTextBoxPanel.Controls.Add(MainTextBox)
// MainForm
let MainForm =
let form = new Form(
Text = "FsEdit"
)
form.Width <- 800
form.Height <- 600
form
MainForm.Controls.Add(MainTextBoxPanel)
FsEdit.fsproj:
<Project Sdk = "Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net7.0-windows</TargetFramework>
<UseWindowsForms>true</UseWindowsForms>
<UseWpf>true</UseWpf>
</PropertyGroup>
<ItemGroup>
<Compile Include = "MainForm.fs" />
<Compile Include = "Program.fs" />
</ItemGroup>
</Project>
Как мне заставить эти объекты управления успешно заполнять своих соответствующих родителей?
@RezaAghaei не лучше: imgur.com/Hk3pnKl
Кроме того, не добавляйте MenuStrip и RichTextBox в одну и ту же панель. Вы можете закрепить MenuStrip в верхней части (в форме) и панель, содержащую RTB для заполнения. Обратите внимание, что вы не можете одновременно закрепить и привязать элементы управления. Выберите один или другой. Вы также можете использовать TableLayoutPanel (который позволяет дочерние элементы управления, а не FLP - напрямую) в качестве контейнера как для MenuStrip, так и для контейнера RichTextBox и пристыковать TLP к Fill
Поведение ожидается для FlowLayoutPanel с направлением потока TopDown. Согласно документации:
Это общее правило для привязки и закрепления в элементе управления FlowLayoutPanel: для вертикального направления потока элемент управления FlowLayoutPanel вычисляет ширину подразумеваемого столбца на основе самого широкого дочернего элемента управления в столбце. Все остальные элементы управления в этом столбце со свойствами Anchor или Dock выравниваются или растягиваются, чтобы соответствовать этому подразумеваемому столбцу.
Если вы хотите закрепить элементы управления сверху и заполнить, вам не нужна FlowLayoutPanel. Вместо этого используйте панель.
Вы можете использовать любой из следующих макетов:
- Form
- Menu (Dock = Top)
- RichTextBox (Dock = Fill)
Или, если по какой-то причине вы хотите разместить и меню, и текстовый редактор на той же панели, что и контейнер:
- Form
- Panel (Dock = Fill)
- Menu (Dock = Top)
- RichTextBox (Dock = Fill)
Важное примечание. Обратите внимание на порядок добавления элементов управления в форму. Z-порядок элементов управления важен и влияет на результат. Элементы управления закрепляются в обратном порядке по оси Z. Возможно, вы захотите узнать больше о том, как закреплять и закреплять элементы управления в Windows Forms.
Узнать больше:
Пример 1. Закрепление меню сверху, RichTextBox для заполнения и StatusStrip снизу
Используйте следующий код для формы, он прикрепит Menu к верхней части, RichTextBox к заполнению и StatusStrip к нижней части без использования какого-либо другого контейнера:
[<RequireQualifiedAccess>]
module FsEdit.MainForm
open System.Windows.Forms
open System.Drawing
// Main menu strip
let private FileMenuTab =
new ToolStripMenuItem(
Text = "File"
)
let private EditMenuTab =
new ToolStripMenuItem(
Text = "Edit"
)
let private AboutMenuTab =
new ToolStripMenuItem(
Text = "About"
)
let private MainMenuStrip =
let m = new MenuStrip(
Text = "MainMenuStrip"
)
m
let private allMenuStripItems =
[|
FileMenuTab
EditMenuTab
AboutMenuTab
|]
|> Array.map (fun tab -> tab :> ToolStripItem)
MainMenuStrip.Items.AddRange(allMenuStripItems)
// Text Editor
let private MainTextBox =
let richTextBox = new RichTextBox(
Text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut a eleifend nunc. Suspendisse non purus varius, ullamcorper arcu et, vehicula lacus. Integer pellentesque facilisis interdum. Aliquam id leo arcu. Nam mauris nisl, semper eget massa sed, aliquam convallis lacus. Etiam a neque blandit, sollicitudin nisl quis, ornare dui. Aliquam nec lorem sit amet arcu iaculis elementum rutrum eu velit. Curabitur dignissim blandit ligula at efficitur. Curabitur id justo quis tortor egestas ultrices. Nam arcu quam, ullamcorper id velit quis, aliquam finibus libero. Pellentesque semper fermentum sem a scelerisque. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae;"
)
richTextBox.Dock <- DockStyle.Fill
richTextBox.AllowDrop <- true
richTextBox.AutoSize <- true
richTextBox
let private MainStatusBar =
let s = new StatusStrip()
s.Dock <- DockStyle.Bottom
s
// MainForm
let MainForm =
let form = new Form(
Text = "FsEdit"
)
form.Width <- 800
form.Height <- 600
form.MinimumSize <- Size(320, 240)
form
MainForm.Controls.Add(MainTextBox)
MainForm.Controls.Add(MainMenuStrip)
MainForm.Controls.Add(MainStatusBar)
Порядок добавления элементов управления также важен и влияет на результат стыковки.
Я думаю, что все эти ответы хороши и обеспечивают хорошую отправную точку для базовой разработки графического интерфейса на F# с помощью WinForms. Я приму этот ответ и, надеюсь, в будущем это поможет другим людям начать разработку графического интерфейса на чистом F#, чего, как мне кажется, очень не хватает в мире разработки F#. Спасибо!
Я бы также добавил акцент на «порядок добавления элементов управления», и это то, с чем разработчикам нужно экспериментировать, когда они добавляют элементы управления в формы или другие элементы управления, поскольку поведение может быть немного неинтуитивным.
Спасибо за отзыв, добавил раздел в ответ, чтобы подчеркнуть важность z-порядка.
Этот пост прямо отвечает на ваш вопрос, и я согласен, имеет смысл пометить его как принятый. Я поделился другим ответом (не как прямое решение вашей проблемы), а как общее решение для проектов, которые разрабатывают многие формы и пользовательский интерфейс; в таких случаях использование дизайнера WinForms очень поможет, так как вы будете использовать дизайнер графического интерфейса и увидите, что вы получите.
Я попробовал как обычный подход Panel
от @Reza Aghaei, так и подход TableLayoutPanel
от @Jimi и решил использовать последний, поскольку я все еще перекрывался с простыми Panel
объектами. Я также добавил StatusStrip
внизу формы, но обнаружил, что если я добавлю его к TableLayoutPanel
, он не будет отображаться правильно, даже если для Dock
установлено значение DocStyle.Bottom
(см. здесь). (Конечно, я обновлю, если найду лучшие программные решения на F# для разработки WinForms.)
Вот скриншот, за которым следует обновленный код:
MainWindow.fs:
[<RequireQualifiedAccess>]
module FsEdit.MainForm
open System.Windows.Forms
open System.Drawing
// Main panel
let private MainTableLayoutPanel =
let t = new TableLayoutPanel()
t.Dock <- DockStyle.Fill
t
// Main menu strip
let private FileMenuTab =
new ToolStripMenuItem(
Text = "File"
)
let private EditMenuTab =
new ToolStripMenuItem(
Text = "Edit"
)
let private AboutMenuTab =
new ToolStripMenuItem(
Text = "About"
)
let private MainMenuStrip =
let m = new MenuStrip(
Text = "MainMenuStrip"
)
m
let private allMenuStripItems =
[|
FileMenuTab
EditMenuTab
AboutMenuTab
|]
|> Array.map (fun tab -> tab :> ToolStripItem)
MainMenuStrip.Items.AddRange(allMenuStripItems)
// Text Editor
let private MainTextBox =
let richTextBox = new RichTextBox(
Text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut a eleifend nunc. Suspendisse non purus varius, ullamcorper arcu et, vehicula lacus. Integer pellentesque facilisis interdum. Aliquam id leo arcu. Nam mauris nisl, semper eget massa sed, aliquam convallis lacus. Etiam a neque blandit, sollicitudin nisl quis, ornare dui. Aliquam nec lorem sit amet arcu iaculis elementum rutrum eu velit. Curabitur dignissim blandit ligula at efficitur. Curabitur id justo quis tortor egestas ultrices. Nam arcu quam, ullamcorper id velit quis, aliquam finibus libero. Pellentesque semper fermentum sem a scelerisque. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae;"
)
richTextBox.Dock <- DockStyle.Fill
// richTextBox.Anchor <- AnchorStyles.Left ||| AnchorStyles.Right ||| AnchorStyles.Top ||| AnchorStyles.Bottom
richTextBox.AllowDrop <- true
richTextBox.AutoSize <- true
richTextBox
let private MainStatusBar =
let s = new StatusStrip()
s.Dock <- DockStyle.Bottom // if anything other than "bottom" does not show up
s
// Add controls to main panel
MainTableLayoutPanel.Controls.Add(MainMenuStrip)
MainTableLayoutPanel.Controls.Add(MainTextBox)
// MainForm
let MainForm =
let form = new Form(
Text = "FsEdit"
)
form.Width <- 800
form.Height <- 600
form.MinimumSize <- Size(320, 240)
form
MainForm.Controls.Add(MainTableLayoutPanel)
MainForm.Controls.Add(MainStatusBar) // Adding to MainTableLayoutPanel makes it size incorrectly even with DockStyle.Bottom
TableLayoutPanel работает, но в этом сценарии совершенно не нужен.
Я обновил ответ примером кода, правильно состыковав его без использования дополнительного контейнера. Посмотрите на скриншот, чтобы увидеть результат. Также обратите внимание на порядок, в котором я добавил элементы управления. Z-порядок элементов управления важен и влияет на результат. Элементы управления закрепляются в обратном порядке по оси Z. Если вы хотите узнать больше, посмотрите ссылки в моем ответе, особенно это на.
Вариант упростить жизнь для тех, кто хочет использовать пользовательский интерфейс WinForms с F#, — это создать пользовательский интерфейс с помощью конструктора Windows Forms и использовать пользовательский интерфейс дизайна с перетаскиванием и всеми функциями времени разработки, а затем добавить ссылку на проект WinForms в проект F#. и добавьте код F#. Следуйте инструкциям, чтобы увидеть, как это работает в действии:
Пример — конструктор пользовательского интерфейса WinForms + код F#
Создайте проект пользовательского интерфейса: создайте библиотеку классов WinForms (.NET 6) и установите для нее имя WinFormsFS.UI
Вы также можете создать проект WinForms или библиотеку управления WinForms и изменить проект и файлы. Единственное, что здесь имеет значение, это тип вывода проекта, который мы хотели бы, чтобы это была библиотека классов, и нам не нужна основная точка входа в этом проекте.
<Project Sdk = "Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>net6.0-windows</TargetFramework>
<UseWindowsForms>true</UseWindowsForms>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
</Project>
Если вы создали проект WinForms, удалите Program.cs или Form1. Для библиотеки классов WinForms удалите Class1, а для библиотеки элементов управления WinForms удалите UserControl1. Нам они не нужны для этого примера.
Добавить форму: добавьте новую форму и установите ее имя MainFormUI
Добавьте элементы управления: удалите MenuStrip, StatusStrip и RichTextBox и позвольте им использовать их имена по умолчанию. MenuStrip будет автоматически закреплен сверху, а StatuStrip — снизу. Вы можете настроить все свойства или добавить пункты меню в полосу меню.
Настройте свойства: прикрепите RichTextBox к Fill (с помощью редактора свойств и настройки свойства Dock или с помощью панели смарт-тегов 🞂 и выбора Dock in parent container).
Изменить модификаторы доступа. Для элементов управления, которые вы хотите использовать в коде F#, вам необходимо изменить модификатор доступа на общедоступный. Для этого выберите RichTextBox и в обозревателе свойств установите для его модификаторов значение Public.
Создайте проект F#: создайте консольное приложение F# с именем WinFormsFS.Code. И установить как стартовый проект. Измените тип вывода на win exe и используйте формы winfows. Файл проекта должен быть таким:
<Project Sdk = "Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0-windows</TargetFramework>
<UseWindowsForms>true</UseWindowsForms>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<Compile Include = "Program.fs" />
</ItemGroup>
</Project>
Добавить ссылку на проект WinForms: щелкните правой кнопкой мыши проект F# и добавьте ссылку на проект WinForms.
Добавьте код для пользовательского интерфейса: добавьте файл MainForm.fs
со следующим кодом:
namespace WinFormsFS.Code
open System.Windows.Forms
open WinFormsFS.UI
type MainForm() as this =
inherit MainFormUI()
do
this.richTextBox1.Text <- "Lorem ipsum!"
Добавьте код для запуска: измените Program.fs следующим образом:
module WinFormsFS.Code.Program
open System
open System.Windows.Forms
[<EntryPoint; STAThread>]
let main args =
Application.EnableVisualStyles()
Application.SetCompatibleTextRenderingDefault(false)
Application.SetHighDpiMode(HighDpiMode.SystemAware) |>ignore
Application.Run (new WinFormsFS.Code.MainForm())
0
Запустите программу, нажав f5, и посмотрите вывод. (Убедитесь, что стартовым проектом является проект F#)
Гораздо более простой подход для сложного пользовательского интерфейса (с использованием дизайнера).
Используйте панель вместо FlowLayoutPanel.