剪贴板复制两次

Clipboard copies twice

(Short Screen-Cast explaining the problem)

我正在制作一个剪贴板程序,它允许您查看剪贴板中的内容。

看起来像这样:

即时复制似乎工作正常。

问题是我希望能够返回到剪贴板中的上一个 img/txt 并使用它 - 那就是我使用复选标记按钮的时候。

它有效,唯一的问题是它将它复制了两次到我正在使用的图像列表/列表框中。当我初始化 listbox/picturebox 时也会发生这种情况。

代码如下:

namespace Clipboard_Wizard
{
public partial class FormMain : Form
{
    //register the program in the clipboard viewer chain
    [DllImport("User32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr SetClipboardViewer(IntPtr hWndNewViewer);

    [DllImport("User32.dll", CharSet = CharSet.Auto)]
    public static extern bool ChangeClipboardChain(IntPtr hWndRemove, IntPtr hWndNewNext);

    private const int WM_DRAWCLIPBOARD = 0x0308;        // WM_DRAWCLIPBOARD message
    private IntPtr _clipboardViewerNext;                // Our variable that will hold the value to identify the next window in the clipboard viewer chain

    private void FormMain_FormClosing(object sender, FormClosingEventArgs e)
    {
        ChangeClipboardChain(this.Handle, _clipboardViewerNext);        // Removes our from the chain of clipboard viewers when the form closes.
    }

    List<Image> img = new List<Image>();
    int ImgCount = 0;
    int ImgIndex = -1;
    Image tmp;

    public FormMain()
    {
        InitializeComponent();

        _clipboardViewerNext = SetClipboardViewer(this.Handle);      // Adds our form to the chain of clipboard viewers.

        //set listbox/picturebox to whatever already on clipboard
        if (Clipboard.ContainsImage())
        {
            tmp = Clipboard.GetImage();
            pictureBox1.Image = tmp;
            img.Add(tmp);
            ImgCount++;
            ImgIndex++;
            btnSlctImg.Enabled = true;
            label3.Text = "Image 1 / 1";
        }
        else if (Clipboard.ContainsText())
        {
            listBox1.Items.Add(Clipboard.GetText());
        }
    }

    // clears everything from the form and the clipboard
    private void btnClear_Click(object sender, EventArgs e)
    {
        Clipboard.Clear();
        listBox1.Items.Clear();
        img.Clear();
        pictureBox1.Image = null;
        ImgCount = 0;
        ImgIndex = -1;
        btnSlctImg.Enabled = false;
    }

    private void btnUpdate_Click(object sender, EventArgs e)
    {
        /*if (Clipboard.ContainsImage())
            {
                tmp = Clipboard.GetImage();
                pictureBox1.Image = tmp;
                img.Add(tmp);
                ImgCount++;
                ImgIndex = ImgCount - 1;  

            }
            else if (Clipboard.ContainsText())
            {
                listBox1.Items.Add(Clipboard.GetText());
                listBox1.TopIndex = listBox1.Items.Count - 1;
            }*/
    }



    private void btnUp_Click(object sender, EventArgs e)
    {
        if(ImgIndex == -1)
        {
            MessageBox.Show("No image.");
        }
        else if (ImgIndex < ImgCount - 1)
        {
            ImgIndex++;
            pictureBox1.Image = img[ImgIndex];
            label3.Text = "Image " + (ImgIndex + 1).ToString() + " / " + ImgCount.ToString() ;
        }
        else
        {
            MessageBox.Show("This is the last image.");
        }
    }

    private void btnDown_Click(object sender, EventArgs e)
    {
        if(ImgIndex == -1)
        {
            MessageBox.Show("No image.");
        }
        else if (ImgIndex > 0)
        {
            ImgIndex--;
            pictureBox1.Image = img[ImgIndex];
            label3.Text = "Image " + (ImgIndex + 1).ToString() + " / " + ImgCount.ToString();
        }
        else
        {
            MessageBox.Show("This is the first image.");
        }
    }

    private void btnDeselect_Click(object sender, EventArgs e)
    {
        listBox1.SelectedIndex = -1;
    }

    //sets clipboard to selected txt from listbox
    private void btnSlct_Click(object sender, EventArgs e)
    {
        string slctTxt = listBox1.SelectedItem.ToString();
        if (slctTxt != null || slctTxt != "")
        {
            Clipboard.SetText(slctTxt);
        }
    }

    //sets clipboard to selected image
    private void btnSlctImg_Click(object sender, EventArgs e)
    {
        Image slctImg = pictureBox1.Image;
        if (slctImg != null)
        {
            Clipboard.SetImage(slctImg); 
        }

    }

    private void listBox1_SelectedIndexChanged_1(object sender, EventArgs e)
    {
        if (listBox1.SelectedIndex != -1)
        {
            btnSlctTxt.Enabled = true;
        }
        else
        {
            btnSlctTxt.Enabled = false;
        }
    }

    protected override void WndProc(ref Message m)
    {
        base.WndProc(ref m);    // Process the message 

        if (m.Msg == WM_DRAWCLIPBOARD)
        {
            //btnUpdate.PerformClick();

            IDataObject iData = Clipboard.GetDataObject();      // Clipboard's data

            if (iData.GetDataPresent(DataFormats.Text))
            {
                string text = (string)iData.GetData(DataFormats.Text);      // Clipboard text
                listBox1.Items.Add(text);
                listBox1.TopIndex = listBox1.Items.Count - 1;
            }
            else if (iData.GetDataPresent(DataFormats.Bitmap))
            {
                tmp = (Bitmap)iData.GetData(DataFormats.Bitmap);   // Clipboard image
                pictureBox1.Image = tmp;
                img.Add(tmp);
                ImgCount++;
                ImgIndex = ImgCount - 1;
                label3.Text = "Image " + ImgCount.ToString() + " / " + ImgCount.ToString();
                btnSlctImg.Enabled = true;  
            }
        }
    }

}
}

更新:问题似乎是每当我调用 Clipboard.SetImage(...)Clipboard.SetText(...) - 它会调用两次。仍然不明白为什么。

我前段时间写了一个剪贴板实用程序并发布在 CodeProject ClipSpy+

我认为它会对你正在做的事情有所帮助!

您已经定义了一个 WndProc 捕获对剪贴板的更改 并将内容添加到列表中。

在你的 btnSlctImg_Click 中做到这一点:

if (slctImg != null)  { Clipboard.SetImage(slctImg);  }

所以,当然剪贴板 改变, WndProc 被触发,当前选择的图像被再次添加到您的列表中..

为避免这种情况,您可能需要测试列表以查看图像或文本是否已在列表中。对于文本,这是微不足道的,但对于图像,这绝非简单。您可能必须创建并存储指纹以确定图像是否已在列表中。

Here is a post 包含为图像创建 MD5 哈希的示例。

一个更简单的技巧是你在Clipboard.SetImage之前的btnSlctImg_Click中设置的标志和在 WndProc 中测试并清除。您仍然可以获得重复项,但前提是相同的数据被用户 在程序之外 复制..