I tried out snapToGrid which I thought might be just the simple solution one wants for lists with side-swipeable entries.
But if I disable tensileDragEnabled then there is no animation when the row snaps to the grid.
Is there a way to disable the tensile scrolling on the X-axis whilst having snapToGrid still animated?
Here's the code:
public class FormScrollingXY extends Form {
public FormScrollingXY() {
setTitle("FormScrollingXY");
setScrollable(false);
setScrollableY(true);
setLayout(new BoxLayout(BoxLayout.Y_AXIS));
for (int rowIndex = 0; rowIndex < 50; rowIndex++) {
Container containerRow = new Container(new BoxLayout(BoxLayout.X_AXIS));
containerRow.setSnapToGrid(true);
containerRow.setScrollableX(true);
containerRow.setTensileDragEnabled(false); // bad behaviour
for (int columnIndex = 0; columnIndex < 3; columnIndex++) {
Container containerColumn = new Container(new FlowLayout()) {
#Override
protected Dimension calcPreferredSize() {
Dimension dimension = new Dimension(super.calcPreferredSize());
dimension.setWidth(containerRow.getWidth());
return dimension;
}
};
containerColumn.add(new Label((rowIndex + 1) + "/" + (columnIndex + 1)));
containerRow.add(containerColumn);
}
add(containerRow);
}
}
}
This is a guess but try doing this to see if it works around this:
Container containerRow = new Container(new BoxLayout(BoxLayout.X_AXIS)) {
public boolean isScrollableY() {
return false;
}
};
Related
I am working on an app on Windows, and I want a Panel that layouts its children like this:
The closest I find in WPF panels is a WrapPanel with a vertical orientation which has a layout like this:
My solution so far has been creating a derived class from WrapPanel and rotating it and each of its children 180 degrees so that my goal is achieved:
public class NotificationWrapPanel : WrapPanel
{
public NotificationWrapPanel()
{
this.Orientation = Orientation.Vertical;
this.RenderTransformOrigin = new Point(.5, .5);
this.RenderTransform = new RotateTransform(180);
}
protected override void OnVisualChildrenChanged(DependencyObject visualAdded, DependencyObject visualRemoved)
{
var addedChild = visualAdded as UIElement;
if (addedChild != null)
{
addedChild.RenderTransformOrigin = new Point(.5, .5);
addedChild.RenderTransform = new RotateTransform(180);
}
base.OnVisualChildrenChanged(addedChild, visualRemoved);
}
}
The problem is that the addedChild.RenderTransform = new RotateTransform(180) part in the overridden OnVisualChildrenChanged does not seem to work. Any solutions (even very unrelated to my approach) are welcomed.
UPDATE:
I re-examined my code and realized I am changing RenderTransform somewhere else, preventing this from working. So my problem is solved. However, I'd appreciate if you offer any solutions that may be more elegant, e.g. using MeasureOverride/ArrangeOverride.
Based on your requirements following is a custom panel that I think should get you what you're after. It arranges child elements in bottom-to-top then right-to-left manner, stacking up to MaxRows elements in a column (or all elements if it is null). All slots are of the same size. It also does take into account elements' Visibility value, i.e. if an item is Hidden, it leaves an empty slot, and if it is Collapsed, it is skipped and next element "jumps" into its place.
public class NotificationWrapPanel : Panel
{
public static readonly DependencyProperty MaxRowsProperty =
DependencyProperty.Register(
name: nameof(MaxRows),
propertyType: typeof(int?),
ownerType: typeof(NotificationWrapPanel),
typeMetadata: new FrameworkPropertyMetadata
{
AffectsArrange = true,
AffectsMeasure = true,
},
validateValueCallback: o => o == null || (int)o >= 1);
public int? MaxRows
{
get { return (int?)GetValue(MaxRowsProperty); }
set { SetValue(MaxRowsProperty, value); }
}
protected override Size MeasureOverride(Size constraint)
{
var children = InternalChildren
.Cast<UIElement>()
.Where(e => e.Visibility != Visibility.Collapsed)
.ToList();
if (children.Count == 0) return new Size();
var rows = MaxRows.HasValue ?
Math.Min(MaxRows.Value, children.Count) :
children.Count;
var columns = children.Count / rows +
Math.Sign(children.Count % rows);
var childConstraint = new Size
{
Width = constraint.Width / columns,
Height = constraint.Height / rows,
};
foreach (UIElement child in children)
child.Measure(childConstraint);
return new Size
{
Height = rows * children
.Cast<UIElement>()
.Max(e => e.DesiredSize.Height),
Width = columns * children
.Cast<UIElement>()
.Max(e => e.DesiredSize.Width),
};
}
protected override Size ArrangeOverride(Size finalSize)
{
var children = InternalChildren
.Cast<UIElement>()
.Where(e => e.Visibility != Visibility.Collapsed)
.ToList();
if (children.Count == 0) return finalSize;
var rows = MaxRows.HasValue ?
Math.Min(MaxRows.Value, children.Count) :
children.Count;
var columns = children.Count / rows +
Math.Sign(children.Count % rows);
var childSize = new Size
{
Width = finalSize.Width / columns,
Height = finalSize.Height / rows
};
for (int i = 0; i < children.Count; i++)
{
var row = i % rows; //rows are numbered bottom-to-top
var col = i / rows; //columns are numbered right-to-left
var location = new Point
{
X = finalSize.Width - (col + 1) * childSize.Width,
Y = finalSize.Height - (row + 1) * childSize.Height,
};
children[i].Arrange(new Rect(location, childSize));
}
return finalSize;
}
}
I'm trying to write a program that plays Yahtzee in a JFrame. Our teacher has a way we're supposed to write it, and it involves adding ConfigurationButtons - a class that extends JButton - to the JFRame. Here is the code I have so far:
builder= new PlayerPanel();
manager = new JFrame("Yahtzee!");
manager.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
manager.setLayout(new GridLayout(TOTAL, numPlayers));
manager.add(builder);
//manager.add(howManyPlayers);
manager.setSize(1200,600);
manager.setVisible(true);
private class PlayerPanel extends JPanel
{
private final int ROWS = 18;
private JLabel[]titles;
private PlayerPanel()
{
for(int j=0;j<numPlayers;j++)
{
for(int i=0;i<ROWS;i++)
{
fields[i][j]=new ConfigurationButton(i,j);
manager.add(fields[i][j]);
}
}
}
I tried implementing this solution but it didn't work- all I get is a blank JFrame.
OK, to fill out your JFrame, see what I have done with the following code, and apply it to your situation.
JFrame manager = new JFrame("Manager");
JPanel panel = new JPanel(new GridLayout(10, 10));
for(int i = 0; i < 10; i++)
{
for(int j = 0; j < 10; j++)
{
JButton button = new JButton(i + " - " + j);
button.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
((JButton)(e.getSource())).setText("Clicked");
}
});
panel.add(button);
}
}
manager.add(panel);
manager.setSize(750, 750);
manager.setVisible(true);
Take note of how the panel is set up in a GridLayout (as you had), but the panel is then added to the JFrame.
You should be able to build your panel based off this example, substituting your custom components where necessary.
I am using C# in VS 2010. I created a custom panel and would like to add this custom panel 9 times so I created a loop to add a copy of the panel 9 times at equal distance from each other. Each panel will have its own text and image. All I'm getting though is a single panel. Any insight would be appreciated
public partial class Form1 : Form
{
int index = 0;
List<CutePanel.CustomPanel> MenuItems = new List<CutePanel.CustomPanel>();
public Form1()
{
InitializeComponent();
for (int i = 0; i < 9; i++)
{
this.cpTest.BackColor = System.Drawing.SystemColors.ActiveCaption;
this.cpTest.LabelText = "My super click text";
this.cpTest.Location = new System.Drawing.Point(12, 12+(64*i));
this.cpTest.Name = "cpTest";
this.cpTest.Size = new System.Drawing.Size(344, 58);
this.cpTest.SuperClick = null;
this.cpTest.TabIndex = 6;
}
cpTest.MouseClick += new MouseEventHandler(cpTest_MouseClick);
cpTest.SuperClick += new EventHandler(cpTest_SuperClick);
cpTest.LabelText = "This is my text.";
MenuItems.Add(cpTest);
}
void cpTest_SuperClick(object sender, EventArgs e)
{
tcTest.SelectedIndex = index++ % 2;
}
void cpTest_MouseClick(object sender, MouseEventArgs e)
{
tcTest.SelectedIndex = index++ % 2;
}
private void customPanel3_MouseClick(object sender, MouseEventArgs e)
{
tcTest.SelectedIndex = index++ % 2;
}
}
Thanks.
You have to make a distinction between your panel class and panel objects, also called instances of this class. Think of the class as a template that serves in creating objects. These objects are created with the new keyword:
for (int i = 0; i < 9; i++)
{
var cp = new CutePanel.CustomPanel();
cp.BackColor = System.Drawing.SystemColors.ActiveCaption;
cp.LabelText = "My super click text";
cp.Location = new System.Drawing.Point(12, 12+(64*i));
cp.Name = "cpTest" + i;
cp.Size = new System.Drawing.Size(344, 58);
cp.SuperClick = null;
cp.TabIndex = 6;
cp.MouseClick += new MouseEventHandler(cpTest_MouseClick);
cp.SuperClick += new EventHandler(cpTest_SuperClick);
cp.LabelText = "This is my text.";
MenuItems.Add(cp);
}
You can also assign it values from the existing panel:
cp.BackColor = cpTest.BackColor;
cp.Size = cpTest.Size;
...
An elegant way of making a duplicate is to include a Clone method in your panel class
public class CustomPanel
{
...
public CustomPanel Clone()
{
var cp = (CustomPanel)this.MemberwiseClone();
cp.Parent = null; // It has not yet been added to the form.
return cp;
}
}
Then
for (int i = 0; i < 9; i++)
{
CustomPanel cp = cpTest.Clone();
// Now only change the differing properties
cp.Location = new System.Drawing.Point(12, 12+(64*i));
cp.Name = "cpTest" + i;
cp.TabIndex += i + 1;
MenuItems.Add(cp);
}
but attention: If the cloned control is a container control containing other controls, you must clones those recursively as well. I.e., you must perform a deep clone! Creating new controls as shown in my first code snippet is safer.
I am working on a "bottom-right docked" popup form.
The layout of the form follows this structure:
Header
Content (text message)
Footer
This form should resize according to its content.
I am using SetBounds to make it grow to the top instead of the bottom (remember that the window is docked bottom-right).
However, when the animation occurs, the footer redraws itself in a very bad way, as its form-relative location is continuously updated.
I provide a sample to give you an idea:
using System.Windows.Forms;
namespace AnimatorTest
{
public class Form3 : Form
{
Timer timer = new Timer();
public Form3()
{
timer.Interval = 30;
timer.Tick += timer_Tick;
// Create 3 test buttons
for (int i = 0; i < 3; i++)
{
Button b = new Button() { Dock = DockStyle.Bottom };
b.Click += (s, e) => timer.Start();
b.Text = "Click and watch how ugly I am during the animation.";
Controls.Add(b);
}
Height = 100;
StartPosition = FormStartPosition.CenterScreen;
}
void timer_Tick(object sender, System.EventArgs e)
{
int desiredHeight = 500;
int difference = desiredHeight - Height;
int change = difference / 6;
if (System.Math.Abs(change) < 1)
{
SetBounds(Left, Top - difference, Width, Height + difference);
timer.Stop();
}
else
{
SetBounds(Left, Top - change, Width, Height + change);
}
}
}
}
I don't really have any idea to work around this.
Thanks.
hi everyone! i am new in wpf so forgive me i want more!!!
i am trying to build an application. i have a treeview that bounded a datasource. it is okay. i have two problems. First; how can i select an item from treeview? i wanna a new window popsup when i select an item; when i click another item; another window popsup. Second problem is that i can not change foreground and font.Thanks everyone in advance
namespace CellBiology
{
public partial class TreeView
{
public TreeView()
{
this.InitializeComponent();
BindTreeView();
}
public void BindTreeView()
{
OleDbConnection con = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=|DataDirectory|\\CellBiology.mdb;Persist Security Info=True");
try
{
con.Open();
OleDbCommand cmd = new OleDbCommand("Select * from Topics", con);
OleDbDataAdapter da = new OleDbDataAdapter(cmd);
DataSet ds = new DataSet();
da.Fill(ds, "Topics");
int row = ds.Tables["Topics"].Rows.Count;
List<MyMenuItem> myList = new List<MyMenuItem>();
if (row > 0)
{
for (int i = 0; i <= row - 1; i++)
{
myList.Add(new MyMenuItem(Convert.ToInt32(ds.Tables["Topics"].Rows[i][0].ToString()), ds.Tables["Topics"].Rows[i][1].ToString(), 0));
for (int j = 0; j <= row - 1; j++)
{
if (ds.Tables["Topics"].Rows[i][0].ToString() == ds.Tables["Topics"].Rows[j][2].ToString())
{
myList.Add(new MyMenuItem(Convert.ToInt32(ds.Tables["Topics"].Rows[j][0].ToString()), ds.Tables["Topics"].Rows[j][1].ToString(), Convert.ToInt32(ds.Tables["Topics"].Rows[i][0].ToString())));
for (int k = 0; k <= row - 1; k++)
{
if (ds.Tables["Topics"].Rows[j][0].ToString() == ds.Tables["Topics"].Rows[k][2].ToString())
{
myList.Add(new MyMenuItem(Convert.ToInt32(ds.Tables["Topics"].Rows[k][0].ToString()),
ds.Tables["Topics"].Rows[k][1].ToString(),
Convert.ToInt32(ds.Tables["Topics"].Rows[j][0].ToString())));
for (int l = 0; l <= row - 1; l++)
if (ds.Tables["Topics"].Rows[k][0].ToString() == ds.Tables["Topics"].Rows[l][2].ToString())
{
myList.Add(new MyMenuItem(Convert.ToInt32(ds.Tables["Topics"].Rows[l][0].ToString()),
ds.Tables["Topics"].Rows[l][1].ToString(), Convert.ToInt32(ds.Tables["Topics"].Rows[k][0].ToString())));
Dictionary<int, TreeViewItem> flattenedTree = new Dictionary<int, TreeViewItem>();
foreach (MyMenuItem item in myList)
{
TreeViewItem treenode = new TreeViewItem();
treenode.Header = item.TopicName;
treenode.Tag = item;
flattenedTree.Add(item.TopicID, treenode);
if (flattenedTree.ContainsKey(item.TopLevelID))
{
flattenedTree[item.TopLevelID].Items.Add(treenode);
}
else
{
myTreeView.Items.Add(treenode);
}
public class MyMenuItem
{
internal int TopicID { get; set; }
internal string TopicName { get; set; }
internal int TopLevelID { get; set; }
internal MyMenuItem(int topicid, string topicname, int toplevelid)
{
TopicID = topicid;
TopicName = topicname;
TopLevelID = toplevelid;
private void myTreeView_SelectedItemChanged(object sender, RoutedEventArgs e)
{
**how can i code here?**
}
}
}
To change the font:
myWindow.FontFamily = new FontFamily("Font Name");
Where the "Font Name" is the name of known font types, eg. "Times New Roman" or "Comic Sans MS". You can find more usages here.
To Change the foreground color:
myWindow.Foreground = new SolidColorBrush(Colors.Red);
Where Colors.Red is can be any 'pre-defined' color of your choice.
Hope it helps.
To answer your "how can i code here?" specifically, you can do the following to access the TreeViewItem:
TreeViewItem selectedTreeViewItem = ((TreeViewItem)e.NewValue);
Once you've got that you can access the data that that item represents via it's DataContext property like so:
MyDataType myData = (MyDataType)selectedTreeViewItem.DataContext;
To popup the new window you can create an instance of your Window sublass and either use ShowDialog, if you want it to be modal, or Show if you want to allow mulitple windows to be opened at the same time.