Использование mtext
для получения меток глобальной оси - хорошая идея. Вы также можете сделать это с легендой. Для меток тиков я бы рекомендовал повернуть их на 90 °. Следовательно, общая читаемость должна была улучшиться. (Может быть даже более точно настроено, чем мое решение.)
Сначала мы помещаем матрицы в список и используем barplot
в lapply
для сохранения ввода.
L <- list(mxCarabooda=mxCarabooda, mxNeerabup=mxNeerabup, mxNowergup=mxNeerabup)
В опции par
мы включаем oma
, чтобы увеличить внешние поля, и xpd
, чтобы разместить текст и легенды где угодно. В barplot
мы отключаем ось x и добавляем их вручную. Используйте сжатие без потерь , установив compression="lzw"
.
tiff("barplot.tiff", width=260, height=100, units='mm', res=300, compression="lzw")
par(mfrow=c(1,3), mar=c(5, 4, 4, 2) + 0.1, oma=c(6, 4, 0, 0), xpd=TRUE)
lapply(seq_along(L), function(x) {
b1 <- barplot(L[[x]], main=gsub("^mx", "", names(L)[x]), beside=TRUE, xaxt="n",
col=c("gray63","gray87"), ylim=c(0,30))
axis(1, at=apply(b1, 2, mean), labels=colnames(L[[x]]),
tick=FALSE, line=FALSE, las=2)
})
mtext('Change in water table at each level of GW cut', side=1, outer=TRUE, line=1) # x-lab
mtext('Profit loss ($m)', side=2, outer=TRUE, line=1) # y-lab
legend(-16.5, -15, bty="n", legend=c('Loss in GM','Loss in adjusted GM'),
col=c("gray63","gray87"), cex=1.2, pch=15, horiz=TRUE, xpd=NA)
dev.off()
Результат
[1124]
Однако, чтобы решение работало Я должен был как минимум удвоить размер tiff
, потому что поля рисунка становятся слишком большими. Тем не менее, соотношение сторон остается тем же, что должно быть более важным . Не могли бы вы проверить это в своем журнале? Возможно, вы могли бы также использовать pdf("barplot.pdf", width=13, height=5)
вместо строки tiff(.)
.
Данные
mxCarabooda <- structure(c(13.47258, 7.430879, 13.47151, 7.53012, 14.83685,
8.940968, 15.37691, 9.617533), .Dim = c(2L, 4L), .Dimnames = list(
c("ChangeNP", "ChangeNP.1"), c("Sl-2 0.1", "S2-3 0.055",
"S3-4 0.056", "S4-5 0.056")))
mxNeerabup <- structure(c(3.499426, 2.232676, 3.499596, 2.239664, 3.836086,
2.566649, 3.995115, 2.725839), .Dim = c(2L, 4L), .Dimnames = list(
c("ChangeNP", "ChangeNP.1"), c("Sl-2 0.1", "S2-3 0.055",
"S3-4 0.056", "S4-5 0.056")))
mxNowergup <- structure(c(3.5135, 1.700544, 3.513586, 1.710387, 3.850266, 2.034689,
4.009113, 2.194351), .Dim = c(2L, 4L), .Dimnames = list(
c("ChangeNP", "ChangeNP.1"), c("Sl-2 0.02", "S2-3 0.01",
"S3-4 0.02", "S4-5 0.02")))
WrapPanel
панель макета, которая позволяет элементам заполняться до тех пор, пока не достигнет края, после чего оставшиеся элементы переходят на следующую строку. Но вы все еще хотите использовать ItemsControl
, чтобы получить все преимущества привязки данных и динамической генерации. Поэтому для этого мы будем использовать свойство ItemsControl.ItemsPanel
, которое позволяет нам указать панель, в которую будут помещены элементы. Давайте снова начнем с выделенного кода:
public partial class Window1 : Window
{
public ObservableCollection<Field> Fields { get; set; }
public Window1()
{
InitializeComponent();
Fields = new ObservableCollection<Field>();
Fields.Add(new Field() { Name = "Username", Length = 100, Required = true });
Fields.Add(new Field() { Name = "Password", Length = 80, Required = true });
Fields.Add(new Field() { Name = "City", Length = 100, Required = false });
Fields.Add(new Field() { Name = "State", Length = 40, Required = false });
Fields.Add(new Field() { Name = "Zipcode", Length = 60, Required = false });
FieldsListBox.ItemsSource = Fields;
}
}
public class Field
{
public string Name { get; set; }
public int Length { get; set; }
public bool Required { get; set; }
}
Здесь мало что изменилось, но я отредактировал примеры полей, чтобы лучше соответствовать вашему примеру. Теперь давайте посмотрим, где происходит магия - XAML для Window
:
<Window x:Class="DataBoundFields.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:DataBoundFields"
Title="Window1" Height="200" Width="300">
<Window.Resources>
<local:BoolToVisibilityConverter x:Key="BoolToVisConverter"/>
</Window.Resources>
<Grid>
<ListBox x:Name="FieldsListBox">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Label Content="{Binding Name}" VerticalAlignment="Center"/>
<TextBox Width="{Binding Length}" Margin="5,0,0,0"/>
<Label Content="*" Visibility="{Binding Required, Converter={StaticResource BoolToVisConverter}}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"
Height="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=ActualHeight}"
Width="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=ActualWidth}"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</Grid>
Во-первых, вы заметите, что ItemTemplate
немного изменился. Метка по-прежнему привязана к свойству name, но теперь ширина текстового поля привязана к свойству length (поэтому вы можете иметь текстовые поля различной длины). Кроме того, я добавил «*» во все обязательные поля, используя упрощенный BoolToVisibilityConverter
(код которого вы можете найти где угодно, и я не буду публиковать здесь).
Главное, на что стоит обратить внимание - это использование WrapPanel
в свойстве ItemsPanel
нашего ListBox
. Это говорит ListBox
, что любые элементы, которые он генерирует, должны быть помещены в горизонтальный свернутый макет (это соответствует вашему скриншоту). Что делает эту работу еще лучше, так это привязка к высоте и ширине на панели - это говорит о том, что «сделайте эту панель того же размера, что и мое родительское окно». Это означает, что когда я изменяю размер Window
, WrapPanel
соответствующим образом корректирует его размер, что приводит к лучшей компоновке элементов.
Не рекомендуется добавлять подобные элементы управления. В WPF вы в идеале должны поместить ListBox (или ItemsControl) и связать свою коллекцию бизнес-объектов как свойство itemsControl.ItemsSource. Теперь определите DataTemplate в XAML для вашего типа DataObject, и все готово. В этом волшебство WPF.
Люди, происходящие из прошлого, имеют тенденцию поступать так, как вы описали, а это неправильно в WPF.
Я бы послушал ответы Чарли и Джоби, но для того, чтобы прямо ответить на вопрос ... (Как добавить элементы управления и расположить их вручную.)
Используйте элемент управления Canvas
вместо а Grid
. Холсты дают элемент управления бесконечное количество места, и позволяют размещать их вручную. Он использует вложенные свойства для отслеживания положения. В коде это будет выглядеть так:
var tb = new TextBox();
myCanvas.Children.Add(tb);
tb.Width = 100;
Canvas.SetLeft(tb, 50);
Canvas.SetTop(tb, 20);
В XAML ...
<Canvas>
<TextBox Width="100" Canvas.Left="50" Canvas.Top="20" />
</Canvas>
Вы также можете расположить их относительно правого и нижнего края. При указании вершины и низа элемент управления будет изменен по вертикали с помощью Canvas. Аналогично для левых и правых.