Autor Beitrag
Kasko
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 111

Win 10
C# C++ (VS 2017/19), (Java, PHP)
BeitragVerfasst: Di 28.07.20 14:37 
Ich möchte eine einfache FileExplorer-Control für einen Videoplayer erstellen.

Hier ist der Code:

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
namespace CustomControls
{
    public interface IScrollable
    {
        float visiblePercent { get; set; }
        float ScrollSpeedMultiplier { get; set; }
        int scrollValue { get; set; }

        CustomScrollBar scrollBar { get; set; }
    }
}

ausblenden volle Höhe C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
namespace CustomControls {
    public partial class ScrollablePanel : UserControl, IScrollable {
        private int _scrollValue;
        private int _totalWheelDelta;

        private int totalWheelDelta {
            get => _totalWheelDelta;
            set {
                SetTotalDelta(value);
            }
        }

        public int Count => panelInner.Controls.Count;

        public bool VisibleControls => panelInner.Controls.OfType<Panel>().All(p => p.Visible);

        public float visiblePercent { get; set; }

        public int scrollValue {
            get { return _scrollValue; }
            set {
                _scrollValue = value;
                if (scrollBar != null && scrollBar.value != _scrollValue)
                    scrollBar.value = _scrollValue;
                SetTotalDeltaByScrollValue(value);
                ScrollThrough();
            }
        }

        [EditorBrowsable(EditorBrowsableState.Always), Browsable(true), DefaultValue(0), Category("Behavior"), Description("Die dazugehörige Scrollbar")]
        public CustomScrollBar scrollBar { get; set; }

        public float ScrollSpeedMultiplier { get; set; }

        public ScrollablePanel() {
            InitializeComponent();
            panelInner.MouseWheel += panelInner_MouseWheel;
        }

        protected override ControlCollection CreateControlsInstance() {
            return new ScrollablePanelElementCollection(thisthis.panelInner);
        }

        private void ScrollablePanel_Load(object sender, EventArgs e) {
            OnControlsChanged();
        }

        private void panelInner_MouseWheel(object sender, MouseEventArgs e) {
            if (visiblePercent >= 1.0f)
                return;

            totalWheelDelta += (int)(e.Delta * ScrollSpeedMultiplier);

            scrollValue = (100 * SystemInformation.MouseWheelScrollLines * totalWheelDelta) / ((Height - panelInner.Height) * SystemData.scrollDeltaModifier);

            if (scrollBar != null)
                scrollValue = scrollBar.value = (100 * SystemInformation.MouseWheelScrollLines * totalWheelDelta) / ((Height - panelInner.Height) * SystemData.scrollDeltaModifier);
        }

        private void ScrollThrough() {
            panelInner.Location = new Point(0, (int)((Height - panelInner.Height) * (scrollValue / 100.0f)));
        }

        private void panelInner_ControlAdded(object sender, ControlEventArgs e) {
            OnControlsChanged();
        }

        private void panelInner_ControlRemoved(object sender, ControlEventArgs e) {
            OnControlsChanged();
        }

        private void OnControlsChanged() {
            int height = int.MinValue;

            foreach (Control control in panelInner.Controls)
                if (control.Visible)
                    height = Math.Max(height, control.Bottom);

            panelInner.Height = height;

            ScrollablePanel_Resize(thisnew EventArgs());

            if (panelInner.Height > 0)
                visiblePercent = (float)Height / panelInner.Height;
            else
                visiblePercent = 1.0f;

            if (scrollBar != null)
                scrollBar.largeChange = (int)(visiblePercent * 100);
        }

        private void ScrollablePanel_Resize(object sender, EventArgs e) {
            panelInner.Width = Width;
        }

        private void SetTotalDelta(int value) {
            int lines = SystemInformation.MouseWheelScrollLines * (value / SystemData.scrollDeltaModifier);
            int deltaHeight = Height - panelInner.Height;

            if (lines > 0)
                _totalWheelDelta = 0;
            else if (lines < deltaHeight)
                _totalWheelDelta = (deltaHeight * SystemData.scrollDeltaModifier) / SystemInformation.MouseWheelScrollLines;
            else
                _totalWheelDelta = value;
        }

        private void SetTotalDeltaByScrollValue(int value) {
            int deltaHeight = Height - panelInner.Height;

            totalWheelDelta = (deltaHeight * SystemData.scrollDeltaModifier * scrollValue) / (100 * SystemInformation.MouseWheelScrollLines);
        }

        public void Clear() {
            panelInner.Controls.Clear();
        }
    }

    public class ScrollablePanelElementCollection : Control.ControlCollection {
        private Control container;

        public ScrollablePanelElementCollection(Control owner, Panel container) : base(owner) { this.container = container; }

        public override void Add(Control value) {
            if (this.Count == 0)
                base.Add(value);
            else
                container.Controls.Add(value);
        }
    }
}


ausblenden volle Höhe C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
namespace MagererPlayer {
    public partial class FileExplorer : UserControl, IScrollable {
        private string _directory;

        public float visiblePercent {
            get => scrollablePanel.visiblePercent;
            set => scrollablePanel.visiblePercent = value;
        }
        
        public int scrollValue {
            get => scrollablePanel.scrollValue;
            set => scrollablePanel.scrollValue = value;
        }
        
        public CustomScrollBar scrollBar {
            get => scrollablePanel.scrollBar;
            set => scrollablePanel.scrollBar = value;
        }

        public string Directory {
            get => this._directory;
            set {
                if (System.IO.Directory.Exists(value)) {
                    this._directory = value;
                    GetDirectory();
                    this.textBoxUrl.Text = this._directory;
                }
            }
        }

        public float ScrollSpeedMultiplier { get; set; }

        public EventHandler OnDirectoryChanged = null;
        public EventHandler<FileSelectedEventArgs> OnFileSelected = null;

        public FileExplorer() {
            InitializeComponent();
        }

        private void FileExplorer_Load(object sender, EventArgs e) {
            AdjustSize();
        }

        private void FileExplorer_Resize(object sender, EventArgs e) {
            AdjustSize();
        }

        private void AdjustSize() {

        }

        private void GetDirectory() {
            scrollablePanel.Clear();
            System.Diagnostics.Trace.WriteLine(this.Directory);

            int offset = System.IO.Directory.GetParent(this.Directory) != null ? 1 : 0;

            int colCount = scrollablePanel.Width / 145;
            string[] dirs = System.IO.Directory.GetDirectories(new string(this.Directory.Where(c => !char.IsControl(c)).ToArray()));
            Array.Sort(dirs);

            if (offset == 1) {
                DirectoryInfo parent = System.IO.Directory.GetParent(this.Directory);
                scrollablePanel.Controls.Add(GetExplorerItem(parent.FullName, ".."new Point(00)));
            }

            for (int i = 0; i < dirs.Length; i++)
                scrollablePanel.Controls.Add(GetExplorerItem(dirs[i], new DirectoryInfo(dirs[i]).Name, new Point(((i + offset) % colCount) * 145, (i + offset) / colCount * 115 + 10)));

            offset += dirs.Length;

            dirs = System.IO.Directory.GetFiles(new string(this.Directory.Where(c => !char.IsControl(c)).ToArray()), "*.mp4", SearchOption.TopDirectoryOnly);
            Array.Sort(dirs);

            for (int i = 0; i < dirs.Length; i++)
                scrollablePanel.Controls.Add(GetExplorerItem(dirs[i], new FileInfo(dirs[i]).Name, new Point(((i + offset) % colCount) * 145, (i + offset) / colCount * 115 + 10)));

            System.Diagnostics.Trace.WriteLine(scrollablePanel.Count);
            System.Diagnostics.Trace.WriteLine(scrollablePanel.VisibleControls);
            this.Invalidate();
        }

        private Panel GetExplorerItem(string path, string text, Point location) {
            BunifuImageButton bunifuImageButton = new BunifuImageButton();
            Label label = new Label();
            Panel panel = new Panel();

            if (System.IO.Directory.Exists(path)) {
                bunifuImageButton.Image = Properties.Resources.folder;
                bunifuImageButton.Click += delegate (object sender, EventArgs e) {
                    this.Directory = path;
                };
            } else if (System.IO.File.Exists(path)) {
                bunifuImageButton.Image = Properties.Resources.video_1;
            }
            
            bunifuImageButton.ImageActive = null;
            bunifuImageButton.Location = new System.Drawing.Point(100);
            bunifuImageButton.Name = "bunifuImageButton";
            bunifuImageButton.Size = new System.Drawing.Size(12570);
            bunifuImageButton.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
            bunifuImageButton.TabStop = false;
            bunifuImageButton.Zoom = 5;
            bunifuImageButton.Cursor = Cursors.Hand; 

            label.Font = new System.Drawing.Font("Century Gothic"9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, 0);
            label.ForeColor = System.Drawing.Color.FromArgb(224224224);
            label.Name = "label";
            label.Dock = DockStyle.Bottom;
            label.Size = new Size(14540);
            label.Text = text;
            label.TextAlign = ContentAlignment.MiddleCenter;
            label.AutoEllipsis = true;

            panel.Controls.Add(bunifuImageButton);
            panel.Controls.Add(label);
            panel.Location = location;
            panel.Name = "panel";
            panel.Size = new System.Drawing.Size(145110);

            return panel;
        }

        private void textBoxUrl_KeyDown(object sender, KeyEventArgs e) {
            if (e.KeyCode == Keys.Enter)
                this.Directory = this.textBoxUrl.Text;
        }
    }

    public class FileSelectedEventArgs : EventArgs {
        public FileInfo File { get; }

        public FileSelectedEventArgs(string path) {
            File = new FileInfo(path);
        }

        public FileSelectedEventArgs(FileInfo info) {
            File = info;
        }
    }
}


Das ScrollablePanel enthält ein innerPanel, das nach oben und unten verschoben wird. Der FileExplorer enthält eine TextView für die URL, einen Button und ein ScrollablePanel.

Mein Problem ist, dass beim Ausführen der Anwendung alles einwandfrei funktioniert. Wenn ich zum übergeordneten Ordner gehe (WICHTIG, KEINE SUBDIR) und dann erneut den Ordner öffne, sind alle Steuerelemente, die vorhanden sein sollten, unsichtbar, wurden jedoch der ControlCollection des innerPanels hinzugefügt.

Dies ist das einzige Ordnerverzeichnis, in dem dieser Fehler auftritt: D:\Serien\Anime\Yu-Gi-Oh! Zexal

Ausgabe von System.Diagnostics aus FileExplorer.GetDirectory:
ausblenden Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
D:\Serien\Anime\Yu-Gi-Oh! Zexal
3
True
D:\Serien\Anime
47
True
D:\Serien\Anime\Yu-Gi-Oh! Zexal
3
True

Screenshot bei Programmstart: ibb.co/2g2Fk1G
Screenshot nach erneut öffnen: ibb.co/J7zKp36

Moderiert von user profile iconTh69: Code-Tags hinzugefügt
Moderiert von user profile iconTh69: Titel geändert (war "Winforms: Controls not visible after added programmatically").
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4315
Erhaltene Danke: 926

Win10
C#, C++ (VS 2015/17)
BeitragVerfasst: Di 28.07.20 16:26 
Hast du dir die relevanten Werte (Location, Visible) mal im Debugger angeschaut?
Durchläuft der Code auch beim 2. Mal den Code mit denselben Werten?

PS: Du solltest deinen Code mal aufräumen (der ist ziemlich unleserlich), z.B. überflüssige Namensbereiche entfernen (und stattdessen [tt]using[/tt]-Deklaration benutzen), Eigenschaften sollten mit einem Großbuchstaben beginnen, ...
Kasko Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 111

Win 10
C# C++ (VS 2017/19), (Java, PHP)
BeitragVerfasst: Di 28.07.20 16:34 
Es sind immer die richtigen und die gleichen Werte.

Zum PS. Die Code-Schnipsel sind aus verschiedenen Projekten. CustomControls und MagererPlayer. CustomControls habe ich erstellt falls ich eigene Controls erstelle die halbwegs generisch nutzbar sind. Vllt kommt der FileExplorer auch eines Tages dort rein. ScrollablePanel ist schon uralt und wurde als eine der ersten Controls hinzugefügt. DAMALS habe ich mich nicht sonderlich um Namenskonventionen gekümmert. Deshalb sind unter anderem manche Properties klein geschrieben
Kasko Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 111

Win 10
C# C++ (VS 2017/19), (Java, PHP)
BeitragVerfasst: Di 28.07.20 22:42 
Okay falsch. Nicht alle Werte sind gleich. Tatsächlich gibt die Visible-Property bei Programmstart false zurück, obwohl bei diesem Versuch die Controls ja sichtbar sind. Nach dem erneuten öffnen gibt sie dann true zurück, aber dann sind sie nicht sichtbar. Beim öffnen des übergeordneten Ordners gibt sie auch true zurück, da ist aber alles sichtbar.

Output: (Ohne den übergeordneten weil sonst zu viel)

ausblenden volle Höhe Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
Url: D:\Serien\Anime\Yu-Gi-Oh! Zexal
Location: {X=0,Y=10}
Bounds: {X=0,Y=10,Width=145,Height=110}
Size: {Width=145, Height=110}
Visible: False

Location: {X=145,Y=10}
Bounds: {X=145,Y=10,Width=145,Height=110}
Size: {Width=145, Height=110}
Visible: False

Location: {X=290,Y=10}
Bounds: {X=290,Y=10,Width=145,Height=110}
Size: {Width=145, Height=110}
Visible: False
CollectionCount: 3


Url: D:\Serien\Anime\Yu-Gi-Oh! Zexal
Location: {X=0,Y=10}
Bounds: {X=0,Y=10,Width=145,Height=110}
Size: {Width=145, Height=110}
Visible: True

Location: {X=145,Y=10}
Bounds: {X=145,Y=10,Width=145,Height=110}
Size: {Width=145, Height=110}
Visible: True

Location: {X=290,Y=10}
Bounds: {X=290,Y=10,Width=145,Height=110}
Size: {Width=145, Height=110}
Visible: True
CollectionCount: 3
Kasko Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 111

Win 10
C# C++ (VS 2017/19), (Java, PHP)
BeitragVerfasst: Mi 29.07.20 01:40 
Okay Fehler gefunden.

Wenn ich den Ordner Ausgewählt habe, musste ich immer nach unten scrollen. Dadurch wurde das innerPanel nach oben geschoben (erste Zeile wurde abgeschnitten/verdeckt) nach dem löschen habe ich das panel nicht wieder zurück geschoben.

Habe Folgendes zur Methode ScrollablePanel.OnControlsChanged hinzugefügt:

ausblenden C#-Quelltext
1:
2:
if (panelInner.Bottom < this.Height)
    panelInner.Location = new Point(panelInner.Location.X, panelInner.Location.Y + Math.Min(Math.Abs(panelInner.Location.Y), this.Height - panelInner.Bottom));